Nguon: Microsoft Learn · .NET 8.0

Phần 6: Phương thức controller và view trong ASP.NET Core

Nguồn: Part 6, controller methods and views in 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:

csharp
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:

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:

cshtml
<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:

html
<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:

csharp
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:

csharp
// 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:

csharp
// 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:

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:

csharp
[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:

cshtml
@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

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):

cshtml
<form asp-action="Edit">

Điều này tạo ra:

html
<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

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.