Академический Документы
Профессиональный Документы
Культура Документы
Домашнее задание 10. Требуется реализовать веб-приложение на ASP.NET Core c использованием средств MVC и Razor для просмотра и
редактирования списков студентов, хранящихся в базе данных. Требования к поддерживаемой функциональности аналогичны указанным
в домашнем задании к занятию 6. Повторное использование кода для доступа к базе данных из вашего решения этой домашней работы
допускается и, более того, приветствуется. В этой работе, веб-приложение должно
содержать следующие страницы и функции:
Обе страницы должны иметь общий дизайн. Заголовок страниц должен содержать название и логотип приложения (можно использовать
произвольное изображение).
Домашнее задание 6. Необходимо написать консольное приложение, позволяющее вести учёт состава учебных групп студентов на
физическом факультете, используя реляционную базу данных для хранения этой информации.
Приложение должно позволять выводить списки студентов по группам, а также добавлять, удалять студентов из группы. Списки студентов
по умолчанию пустые и будут редактироваться в приложении.
Приложение должно быть рассчитано на работу с произвольным количеством групп, но для удобства, следующие группы должны быть
автоматически добавлены в базу данных при первом запуске приложения средствами Entity Framework:
● радиофизика,
● микроэлектроника,
● общая физика.
FirstName = firstName;
LastName = lastName;
FathersName = fathersName;
}
public override string ToString() => LastName + " " + FirstName + " " + FathersName;
}
Доступ к данным (контекст)
group.HasData(
new Group() { Id = 1, Name = "Радиофизика" },
new Group() { Id = 2, Name = "Микроэлектроника" },
new Group() { Id = 3, Name = "Общая физика" }
);
}
await _db.SaveChangesAsync();
return student.Id;
}
existingStudent.FullName = student.FullName;
existingStudent.GroupId = student.GroupId;
await _db.SaveChangesAsync();
return true;
}
Удаление студента:
Контроллер (1/2)
// GET /student
// GET /student/index
[HttpGet]
public async Task<IActionResult> Index()
{
// ... получить списки всех групп и студентов
return View(...);
}
// ДОБАВЛЕНИЕ СТУДЕНТА
// РЕДАКТИРОВАНИЕ СТУДЕНТА
return View(formVm);
}
return RedirectToAction(nameof(Index));
}
else
return RedirectToAction(nameof(Edit), new { id = student.Id });
}
// УДАЛЕНИЕ СТУДЕНТА
// POST /student/delete/1
[HttpPost]
public async Task<IActionResult> Delete(int id)
{
// ... удалить студента
return RedirectToAction(nameof(Index));
}
}
Список студентов (контроллер)
[HttpGet]
public async Task<IActionResult> Index()
{
var groups = await _studentStore.AllGroupsWithStudentsAsync();
ViewModel -- объект предназначенный для передачи данных в представление. Содержит только те данные, которые нужны
🛈 конкретному представлению, в том формате, который удобен для этого представления.
Список студентов (внедрение зависимостей)
services.AddDbContext<StudentsContext>(opts =>
opts.UseSqlServer(Configuration.GetConnectionString("Default")));
services.AddTransient<StudentStore>();
}
// ...
}
// ...
}
// ...
}
Список студентов (модели представления)
<div class="student-list">
@foreach (var student in group.Students)
{
<div class="student">
<span class="student-name">@student.FullName</span>
<a asp-action="Edit" asp-route-id="@student.Id" class="action">Редактировать</a>
<a asp-action="Delete" asp-route-id="@student.Id" class="action">Удалить</a>
</div>
<!-- 🤔 -->
}
</div>
</div>
}
Обратите внимание на использование атрибутов asp-* (tag helpers) в элементах ссылок. Используйте их, или @Html.* (html
⚠ helpers) для того, чтобы указать ссылки на экшены, вместо “зашитых” в разметку URL-ов.
Список студентов (представление 2/2)
Отправка формы на сервер приводит к GET-запросу или POST-запросу в браузере в зависимости от значения атрибута method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([FromForm]StudentFormVM vm) // тип такой же, как тип модели представления в GET
{
var studentVm = vm.Student;
if (ModelState.IsValid)
{
await _studentStore.AddStudentAsync(studentVm.ToStudent());
return RedirectToAction(nameof(Index));
}
else
{
}
return View(await CreateStudentFormVMAsync(studentVm)); // возвращаем форму так же, как в GET-запросе, 🤔
}
<input asp-for="Student.Id" type="hidden" /> <!-- чтобы ID студента пришел на сервер при отправке формы -->
<div class="control-group">
<label asp-for="Student.FullName" class="control-label"> <!-- asp-for чтобы при нажатии на label, фокус перешел в input -->
<abbr title="Фамилия Имя Отчество">ФИО</abbr>:
</label>
<input asp-for="Student.FullName" class="control" placeholder="Имя студента" />
<span asp-validation-for="Student.FullName" class="control-error"></span>
</div>
<div class="control-group">
<label asp-for="Student.GroupId" class="control-label">
Группа:
</label>
<select asp-for="Student.GroupId" asp-items="Model.AllGroups" class="control"></select>
<span asp-validation-for="Student.GroupId" class="control-error"></span>
</div>
<div class="form-actions">
<input class="action-button" type="submit" value="Сохранить" />
<a class="action-button" asp-action="Index">Отмена</a>
</div>
</form>
Выпадающие списки
// Определен в Microsoft.AspNetCore.Mvc.Rendering
// https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.rendering.selectlistitem?view=aspnetcore-5.0
// ...
}
<div class="control-group">
<!-- ... -->
<select asp-for="Student.GroupId" asp-items="Model.AllGroups" class="control"></select>
<!-- ... -->
</div>
Валидация форм (1/3)
<div class="control-group">
<label asp-for="Student.FullName" class="control-label">
<abbr title="Фамилия Имя Отчество">ФИО</abbr>:
</label>
<input asp-for="Student.FullName" class="control" placeholder="Имя студента" />
<span asp-validation-for="Student.FullName" class="control-error"></span> <-- (3) -->
</div>
Валидация входных данных на сервере обязательна, даже если эти данные уже валидировались на клиенте, или UI клиента не
⚠ позволяет ввести некорректные данные. Отправка формы -- это обычный POST-запрос, который можно сделать напрямую, в
обход UI отображаемого в браузере.
Валидация форм (2/3)
// Определены в Microsoft.AspNetCore.Mvc.*
// ...
}
// ...
}
Шаг 1.
Пользователь логиниться на сайте https://good-guys.com.
Сайт устанавливает аутентификационную куку, идентифицирующую пользователя.
Браузер будет отправлять эту куку вместе с любым запросом с доменом good-guys.com в
адресе.
Cross-Site Request Forgery (2/4)
Шаг 2.
Злоумышленник заманивает этого пользователя на свой сайт https://shady-biz.io.
На сайте показана внешне форма, никак не связанная с https://good-guys.com.
Пользователя подбивают отправить форму.
Итог.
Пользователь, сам того не подозревая, перевел деньги на номер злоумышленника.
Отправка формы с сайта https://shady-biz.io на сайт https://good-guys.com пройдет успешно и от имени пользователя, т.к. браузер отправит
легитимную авторизационную куку установленную сайтом good-guys.com при POST-запросе на это доменное имя.
https://en.wikipedia.org/wiki/Cross-site_request_forgery
Cross-Site Request Forgery (3/4)
Cross-Site Request Forgery (4/4)
// ...
}
Подробнее: https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
Подтверждение повторной отправки формы
[HttpPost]
[ValidateAntiForgeryToken]
[ExportModelState]
public async Task<IActionResult> Create([FromForm]StudentFormVM vm)
{
var studentVm = vm.Student;
if (ModelState.IsValid)
{
await _studentStore.AddStudentAsync(studentVm.ToStudent());
return RedirectToAction(nameof(Index));
}
else
{
return View(await CreateStudentFormVMAsync(studentVm));
[HttpPost]
[ValidateAntiForgeryToken]
[ExportModelState]
public async Task<IActionResult> Edit([FromForm] StudentFormVM vm) // модель такая же как для добавления Студента
{
var studentVm = vm.Student;
if (ModelState.IsValid)
{
var isUpdated = await _studentStore.UpdateStudentAsync(studentVm.Id, studentVm.ToStudent());
if (!isUpdated)
return NotFound();
return RedirectToAction(nameof(Index));
}
else
{
return RedirectToAction(nameof(Edit), new { id = studentVm.Id });
}
}
}
Редактирование студента (повторное использование представления)
Create.cshtml
@model StudentsWeb.Models.StudentForm.StudentFormVM
@{ ViewData["Title"] = "Новый студент"; }
Edit.cshtml
@model StudentsWeb.Models.StudentForm.StudentFormVM
@{ ViewData["Title"] = "Редактирование студента"; }
_StudentForm.cshtml
@model StudentsWeb.Models.StudentForm.StudentFormVM
_ViewStart.cshtml
@{ Layout = "_Layout"; }
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cтуденты - @ViewData["Title"]</title>
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<div class="header">
<div class="logo"></div>
<div class="title">Списки студентов</div>
</div>
<div class="content">
@RenderBody() <!-- сюда будет подставлена разметка представления -->
</div>
GET:
<a asp-action="Delete" asp-route-id="@student.Id" class="action">Удалить</a>
POST:
<form method="POST" class="action-form">
<button class="button-as-link" asp-action="Delete" asp-route-id="@student.Id">Удалить</button>
</form>
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-method
https://softwareengineering.stackexchange.com/questions/114156/why-are-there-are-no-put-and-delete-methods-on-html-forms