Phần 6: Phương thức controller và view trong ASP.NET Core
Tổng quan
Tutorial này đề cập cách làm việc với phương thức controller và view trong ASP.NET Core MVC, tập trung vào việc trình bày dữ liệu và xử lý form.
Data Annotations để định dạng Model
Thêm attribute Display và DataType
Để cải thiện việc trình bày model Movie, thêm data annotation vào file Models/Movie.cs:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}Các Annotation quan trọng:
[Display(Name = "Release Date")]- Chỉ định tên hiển thị cho trường thay vì "ReleaseDate"[DataType(DataType.Date)]- Chỉ định kiểu dữ liệu, ngăn thông tin thời gian không được hiển thị[Column(TypeName = "decimal(18, 2)")]- Đảm bảo Entity Framework Core ánh xạ chính xác Price sang kiểu currency trong database
Tag Helpers và tạo URL
Anchor Tag Helper
Các link Edit, Details và Delete trong Views/Movies/Index.cshtml được tạo bằng Anchor Tag Helper:
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> | <a asp-action="Details" asp-route-id="@item.Id">Details</a> | <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
AnchorTagHelper tự động tạo các thuộc tính HTML href dựa trên controller action và route ID. Ví dụ, đoạn này tạo ra:
<a href="/Movies/Edit/4"> Edit </a> | <a href="/Movies/Details/4"> Details </a> | <a href="/Movies/Delete/4"> Delete </a>
Cấu hình Routing
Mẫu routing mặc định trong Program.cs:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.WithStaticAssets();Mẫu này dịch https://localhost:5001/Movies/Edit/4 thành một request đến action method Edit của controller Movies với tham số Id là 4.
Các phương thức Edit Action
HTTP GET Edit Method
Lấy movie và điền vào edit form:
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}HTTP POST Edit Method
Xử lý các giá trị movie đã được post:
// POST: Movies/Edit/5
// Để bảo vệ khỏi các cuộc tấn công overposting, hãy bật các thuộc tính cụ thể mà bạn muốn bind.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}Các tính năng chính:
- Attribute
[HttpPost]giới hạn phương thức chỉ nhận POST request [ValidateAntiForgeryToken]ngăn chặn các cuộc tấn công cross-site request forgery (XSRF/CSRF)- Attribute
[Bind]bảo vệ chống over-posting bằng cách chỉ định thuộc tính nào có thể được bind ModelState.IsValidxác thực dữ liệu form trước khi lưuSaveChangesAsync()lưu các thay đổi vào database- Chuyển hướng đến action
Indexsau khi cập nhật thành công
Bảo vệ Over-Posting
Attribute [Bind] giới hạn các thuộc tính nào có thể được sửa đổi:
[Bind("Id,Title,ReleaseDate,Genre,Price,Rating")]Điều này ngăn chặn việc sửa đổi trái phép các thuộc tính không được liệt kê rõ ràng. Các phương pháp thay thế bao gồm sử dụng ViewModel.
Edit View Template (Mẫu Edit View)
Scaffolded Edit view sử dụng Tag Helper để tạo form:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}Các phần tử Tag Helper
- Label Tag Helper - Render label trường với tên thích hợp
- Input Tag Helper - Tạo các phần tử HTML
<input> - Validation Tag Helper - Hiển thị thông báo lỗi xác thực
Form Tag Helper và bảo vệ CSRF
Form Tag Helper tạo ra antiforgery token (mã thông báo chống giả mạo):
<form asp-action="Edit">
Điều này tạo ra:
<form action="/Movies/Edit/7" method="post">
<!-- các phần tử form -->
<input name="__RequestVerificationToken" type="hidden" value="..." />
</form>Token ẩn phải khớp với xác thực phía server trong attribute [ValidateAntiForgeryToken].
Client-Side và Server-Side Validation
- Client-side validation (xác thực phía client) kiểm tra các quy tắc xác thực trước khi gửi form
- Server-side validation (xác thực phía server) qua
ModelState.IsValidphát hiện các giá trị được post không hợp lệ - Các lỗi xác thực được hiển thị lại bằng Validation Tag Helper
- Nếu JavaScript bị tắt, xác thực phía server vẫn bảo vệ ứng dụng
Thực hành tốt nhất cho việc sửa đổi dữ liệu
Quy tắc bảo mật: Sửa đổi dữ liệu trong HTTP GET method là rủi ro bảo mật và vi phạm nguyên tắc REST.
- GET request phải là thao tác an toàn không có tác dụng phụ
- Chỉ các phương thức
[HttpPost]mới nên sửa đổi dữ liệu [HttpGet]là mặc định và không cần chỉ định rõ ràng- Luôn sử dụng
[ValidateAntiForgeryToken]trên các POST method sửa đổi dữ liệu