ASP.NET Core: Model Validation trong ASP.NET Core MVC và Razor Pages


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

zzzBài viết này giải thích cách xác thực dữ liệu đầu vào của người dùng trong ứng dụng ASP.NET Core MVC hoặc Razor Pages.

Xem hoặc tải xuống mã mẫu  ( cách tải xuống ).

Trạng thái mô hình

Trạng thái mô hình biểu thị các lỗi xuất phát từ hai hệ thống con: liên kết mô hình và xác thực mô hình. Lỗi bắt nguồn từ việc  liên kết mô hình  nói chung là lỗi chuyển đổi dữ liệu. Ví dụ: nhập "x" vào trường số nguyên. Việc xác thực mô hình xảy ra sau khi liên kết mô hình và báo cáo các lỗi trong đó dữ liệu không tuân thủ các quy tắc kinh doanh. Ví dụ: nhập số 0 vào trường dự kiến ​​xếp hạng từ 1 đến 5.

Cả liên kết mô hình và xác thực mô hình đều xảy ra trước khi thực hiện hành động của bộ điều khiển hoặc phương thức xử lý Razor Pages. Đối với các ứng dụng web, trách nhiệm của ứng dụng là kiểm tra  ModelState.IsValid và phản ứng thích hợp. Các ứng dụng web thường hiển thị lại trang có thông báo lỗi, như trong ví dụ về Trang Razor sau:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Đối với ASP.NET Core MVC có bộ điều khiển và dạng xem, ví dụ sau đây cho biết cách kiểm tra  ModelState.IsValid bên trong hành động của bộ điều khiển:

public async Task<IActionResult> Create(Movie movie)
{
    if (!ModelState.IsValid)
    {
        return View(movie);
    }

    _context.Movies.Add(movie);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}

Bộ điều khiển API Web không phải kiểm tra xem  ModelState.IsValid chúng có  thuộc tính [ApiController] hay không  . Trong trường hợp đó, phản hồi HTTP 400 tự động chứa chi tiết lỗi sẽ được trả về khi trạng thái mô hình không hợp lệ. Để biết thêm thông tin, hãy xem  Phản hồi HTTP 400 tự động .

Chạy lại xác thực

Quá trình xác thực diễn ra tự động nhưng bạn có thể muốn lặp lại quá trình này theo cách thủ công. Ví dụ: bạn có thể tính toán một giá trị cho một thuộc tính và muốn chạy lại quá trình xác thực sau khi đặt thuộc tính đó thành giá trị được tính toán. Để chạy lại xác thực, hãy gọi  ModelStateDictionary.ClearValidationState  để xóa xác thực cụ thể cho mô hình đang được xác thực, theo sau là  TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Thuộc tính xác thực

Thuộc tính xác thực cho phép bạn chỉ định quy tắc xác thực cho thuộc tính mô hình. Ví dụ sau đây từ  ứng dụng mẫu  hiển thị một lớp mô hình được chú thích bằng các thuộc tính xác thực. Thuộc tính này  [ClassicMovie] là thuộc tính xác thực tùy chỉnh và các thuộc tính khác được tích hợp sẵn. Không hiển thị là  [ClassicMovieWithClientValidator], hiển thị một cách khác để triển khai thuộc tính tùy chỉnh.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Thuộc tính tích hợp

Dưới đây là một số thuộc tính xác thực tích hợp:

  • [ValidateNever] : Cho biết rằng một thuộc tính hoặc tham số cần được loại trừ khỏi quá trình xác thực.
  • [CreditCard] : Xác thực rằng chỗ nghỉ có định dạng thẻ tín dụng. Yêu cầu  các phương thức bổ sung xác thực jQuery .
  • [So sánh] : Xác thực rằng hai thuộc tính trong một mô hình khớp nhau.
  • [EmailAddress] : Xác thực rằng thuộc tính có định dạng email.
  • [Điện thoại] : Xác thực rằng thuộc tính có định dạng số điện thoại.
  • [Phạm vi] : Xác thực rằng giá trị thuộc tính nằm trong phạm vi được chỉ định.
  • [RegularExpression] : Xác thực rằng giá trị thuộc tính khớp với một biểu thức chính quy được chỉ định.
  • [Bắt buộc] : Xác thực rằng trường không rỗng. Xem  [Required] thuộc tính  để biết chi tiết về hành vi của thuộc tính này.
  • [StringLength] : Xác thực rằng giá trị thuộc tính chuỗi không vượt quá giới hạn độ dài được chỉ định.
  • [Url] : Xác thực rằng thuộc tính có định dạng URL.
  • [Remote] : Xác thực đầu vào trên máy khách bằng cách gọi một phương thức hành động trên máy chủ. Xem  [Remote] thuộc tính  để biết chi tiết về hành vi của thuộc tính này.

Bạn có thể tìm thấy danh sách đầy đủ các thuộc tính xác thực trong  không gian tên System.ComponentModel.DataAnnotations  .

Thông báo lỗi

Thuộc tính xác thực cho phép bạn chỉ định thông báo lỗi sẽ được hiển thị cho đầu vào không hợp lệ. Ví dụ:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Trong nội bộ, các thuộc tính gọi  String.Format  với một trình giữ chỗ cho tên trường và đôi khi là các trình giữ chỗ bổ sung. Ví dụ:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

Khi áp dụng cho một  Name thuộc tính, thông báo lỗi được tạo bởi mã trước đó sẽ là "Độ dài tên phải nằm trong khoảng từ 6 đến 8.".

Để tìm hiểu tham số nào được chuyển đến  String.Format cho thông báo lỗi của một thuộc tính cụ thể, hãy xem  mã nguồn DataAnnotations .

Sử dụng tên thuộc tính JSON trong các lỗi xác thực

Theo mặc định, khi xảy ra lỗi xác thực, quá trình xác thực mô hình sẽ tạo ra ModelStateDictionary  với  tên thuộc tính là khóa lỗi. Một số ứng dụng, chẳng hạn như ứng dụng một trang, được hưởng lợi từ việc sử dụng tên thuộc tính JSON cho các lỗi xác thực được tạo từ API Web. Đoạn mã sau định cấu hình xác thực để sử dụng  SystemTextJsonValidationMetadataProvider  nhằm sử dụng tên thuộc tính JSON:

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Đoạn mã sau định cấu hình xác thực để sử dụng  NewtonsoftJsonValidationMetadataProvider  để sử dụng tên thuộc tính JSON khi sử dụng  Json.NET :

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Để biết ví dụ về chính sách sử dụng vỏ lạc đà, hãy xem  Program.cs trên GitHub .

Các loại tham chiếu không thể rỗng và thuộc tính [Bắt buộc]

Hệ thống xác thực xử lý các tham số không thể rỗng hoặc các thuộc tính bị ràng buộc như thể chúng có một  [Required(AllowEmptyStrings = true)] thuộc tính. Bằng cách  bật  Nullable ngữ cảnh , MVC ngầm bắt đầu xác thực các thuộc tính hoặc tham số không thể rỗng như thể chúng đã được gán cho thuộc  [Required(AllowEmptyStrings = false)] tính đó. Hãy xem xét đoạn mã sau:

public class Person
{
    public string Name { get; set; }
}

Nếu ứng dụng được xây dựng bằng  <Nullable>enable</Nullable>, thì việc thiếu giá trị  Name trong JSON hoặc bài đăng biểu mẫu sẽ dẫn đến lỗi xác thực. Sử dụng loại tham chiếu có thể rỗng để cho phép chỉ định các giá trị rỗng hoặc thiếu cho  Name thuộc tính:

public class Person
{
    public string? Name { get; set; }
}

Hành vi này có thể bị vô hiệu hóa bằng cách định cấu hình  SuppressImplicitRequiredAttributionForNonNullableReferenceTypes  trong  Program.cs:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

[Bắt buộc] xác thực trên máy chủ

Trên máy chủ, giá trị bắt buộc được coi là thiếu nếu thuộc tính rỗng. Trường không thể rỗng luôn hợp lệ và  [Required] thông báo lỗi của thuộc tính không bao giờ được hiển thị.

Tuy nhiên, liên kết mô hình cho thuộc tính không thể rỗng có thể không thành công, dẫn đến thông báo lỗi như  The value '' is invalid. Để chỉ định thông báo lỗi tùy chỉnh cho việc xác thực phía máy chủ đối với các loại không thể rỗng, bạn có các tùy chọn sau:

  • Đặt trường thành null (ví dụ:  decimal? thay vì  decimal). Các loại giá trị Nullable<T>  được xử lý như các loại giá trị nullable tiêu chuẩn.

  • Chỉ định thông báo lỗi mặc định sẽ được sử dụng bởi liên kết mô hình, như trong ví dụ sau:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

    Để biết thêm thông tin về các lỗi liên kết mô hình mà bạn có thể đặt thông báo mặc định, hãy xem  DefaultModelBindingMessageProvider .

[Bắt buộc] xác thực trên máy khách

Các loại và chuỗi không thể rỗng được xử lý khác nhau trên máy khách so với máy chủ. Trên máy khách:

  • Một giá trị chỉ được coi là hiện diện nếu đầu vào được nhập cho nó. Do đó, quá trình xác thực phía máy khách xử lý các loại không thể rỗng giống như các loại có thể rỗng.
  • Khoảng trắng trong trường chuỗi được coi là đầu vào hợp lệ theo  phương thức yêu cầu Xác thực jQuery  . Xác thực phía máy chủ coi trường chuỗi bắt buộc là không hợp lệ nếu chỉ nhập khoảng trắng.

Như đã lưu ý trước đó, các loại không thể rỗng được xử lý như thể chúng có một  [Required(AllowEmptyStrings = true)] thuộc tính. Điều đó có nghĩa là bạn nhận được xác thực phía máy khách ngay cả khi bạn không áp dụng  [Required(AllowEmptyStrings = true)] thuộc tính. Nhưng nếu bạn không sử dụng thuộc tính này, bạn sẽ nhận được thông báo lỗi mặc định. Để chỉ định một thông báo lỗi tùy chỉnh, hãy sử dụng thuộc tính.

Thuộc tính [Từ xa]

Thuộc  tính [Remote]  triển khai xác thực phía máy khách yêu cầu gọi một phương thức trên máy chủ để xác định xem trường đầu vào có hợp lệ hay không. Ví dụ: ứng dụng có thể cần xác minh xem tên người dùng đã được sử dụng hay chưa.

Để thực hiện xác thực từ xa:

  1. Tạo một phương thức hành động để JavaScript gọi. Phương thức từ xa Xác thực jQuery   mong đợi phản hồi JSON:

    • true có nghĩa là dữ liệu đầu vào là hợp lệ.
    • falseundefined, hoặc  null có nghĩa là thông tin nhập vào không hợp lệ. Hiển thị thông báo lỗi mặc định.
    • Bất kỳ chuỗi nào khác có nghĩa là dữ liệu đầu vào không hợp lệ. Hiển thị chuỗi dưới dạng thông báo lỗi tùy chỉnh.

    Dưới đây là ví dụ về phương thức hành động trả về thông báo lỗi tùy chỉnh:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Trong lớp mô hình, chú thích thuộc tính bằng  [Remote] thuộc tính trỏ đến phương thức hành động xác thực, như trong ví dụ sau:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; } = null!;
    

Xác thực phía máy chủ  cũng cần được triển khai cho các máy khách đã tắt JavaScript.

Các trường bổ sung

Thuộc  tính Bổ sung  của  [Remote] thuộc tính cho phép bạn xác thực các kết hợp trường dựa trên dữ liệu trên máy chủ. Ví dụ: nếu  User mô hình có  FirstName và  LastName thuộc tính, bạn có thể muốn xác minh rằng không có người dùng hiện tại nào có cặp tên đó. Ví dụ sau đây cho thấy cách sử dụng  AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;

AdditionalFields có thể được đặt rõ ràng thành chuỗi "FirstName" và "LastName", nhưng việc sử dụng  toán tử nameof  sẽ đơn giản hóa việc tái cấu trúc sau này. Phương thức hành động để xác thực này phải chấp nhận cả hai  firstName và  lastName đối số:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Khi người dùng nhập họ hoặc tên, JavaScript sẽ thực hiện lệnh gọi từ xa để xem liệu cặp tên đó đã được sử dụng hay chưa.

Để xác thực hai hoặc nhiều trường bổ sung, hãy cung cấp chúng dưới dạng danh sách được phân cách bằng dấu phẩy. Ví dụ: để thêm  MiddleName thuộc tính vào mô hình, hãy đặt  [Remote] thuộc tính như trong ví dụ sau:

[Remote(action: "VerifyName", controller: "Users",
    AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, giống như tất cả các đối số thuộc tính, phải là một biểu thức không đổi. Do đó, không sử dụng  chuỗi nội suy  hoặc gọi  Join  để khởi tạo  AdditionalFields.

Các lựa chọn thay thế cho các thuộc tính tích hợp

Nếu bạn cần xác thực không được cung cấp bởi các thuộc tính tích hợp, bạn có thể:

Thuộc tính tùy chỉnh

Đối với các trường hợp mà thuộc tính xác thực tích hợp không xử lý được, bạn có thể tạo thuộc tính xác thực tùy chỉnh. Tạo một lớp kế thừa từ  ValidationAttribution và ghi đè  phương thức IsValid  .

Phương thức này  IsValid chấp nhận một đối tượng có tên  value , là đầu vào được xác thực. Quá tải cũng chấp nhận  đối tượng ValidationContext  , đối tượng này cung cấp thông tin bổ sung, chẳng hạn như phiên bản mô hình được tạo bởi liên kết mô hình.

Ví dụ sau đây xác thực rằng ngày phát hành của một bộ phim thuộc  thể loại Cổ điển  không muộn hơn một năm cụ thể. Thuộc  [ClassicMovie] tính:

  • Chỉ chạy trên máy chủ.
  • Đối với phim Cổ điển, xác nhận ngày phát hành:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
        => Year = year;

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult? IsValid(
        object? value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value!).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

Biến  movie trong ví dụ trước biểu thị một  Movie đối tượng chứa dữ liệu từ quá trình gửi biểu mẫu. Khi xác thực không thành công,  Kết quả xác thực  có thông báo lỗi sẽ được trả về.

Đối tượng có thể xác thực được

Ví dụ trước chỉ hoạt động với  Movie các loại. Một tùy chọn khác để xác thực cấp độ lớp là triển khai  IValidatableObject  trong lớp mô hình, như trong ví dụ sau:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Xác thực tùy chỉnh

Đoạn mã sau cho biết cách thêm lỗi mô hình sau khi kiểm tra mô hình:

if (Contact.Name == Contact.ShortName)
{
    ModelState.AddModelError("Contact.ShortName", 
                             "Short name can't be the same as Name.");
}

Đoạn mã sau thực hiện kiểm tra xác thực trong bộ điều khiển:

if (contact.Name == contact.ShortName)
{
    ModelState.AddModelError(nameof(contact.ShortName),
                             "Short name can't be the same as Name.");
}

Đoạn mã sau xác minh số điện thoại và email là duy nhất:

public async Task<IActionResult> OnPostAsync()
{
    // Attach Validation Error Message to the Model on validation failure.          

    if (Contact.Name == Contact.ShortName)
    {
        ModelState.AddModelError("Contact.ShortName", 
                                 "Short name can't be the same as Name.");
    }

    if (_context.Contact.Any(i => i.PhoneNumber == Contact.PhoneNumber))
    {
        ModelState.AddModelError("Contact.PhoneNumber",
                                  "The Phone number is already in use.");
    }
    if (_context.Contact.Any(i => i.Email == Contact.Email))
    {
        ModelState.AddModelError("Contact.Email", "The Email is already in use.");
    }

    if (!ModelState.IsValid || _context.Contact == null || Contact == null)
    {
        // if model is invalid, return the page with the model state errors.
        return Page();
    }
    _context.Contact.Add(Contact);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Đoạn mã sau thực hiện kiểm tra xác thực trong bộ điều khiển:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,ShortName,Email,PhoneNumber")] Contact contact)
{
    // Attach Validation Error Message to the Model on validation failure.
    if (contact.Name == contact.ShortName)
    {
        ModelState.AddModelError(nameof(contact.ShortName),
                                 "Short name can't be the same as Name.");
    }

    if (_context.Contact.Any(i => i.PhoneNumber == contact.PhoneNumber))
    {
        ModelState.AddModelError(nameof(contact.PhoneNumber),
                                  "The Phone number is already in use.");
    }
    if (_context.Contact.Any(i => i.Email == contact.Email))
    {
        ModelState.AddModelError(nameof(contact.Email), "The Email is already in use.");
    }

    if (ModelState.IsValid)
    {
        _context.Add(contact);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(contact);
}

Việc kiểm tra số điện thoại hoặc email duy nhất cũng thường được thực hiện bằng  xác thực từ xa .

Xác thựcKết quả

Hãy xem xét các tùy chỉnh sau  ValidateNameAttribute:

public class ValidateNameAttribute : ValidationAttribute
{
    public ValidateNameAttribute()
    {
        const string defaultErrorMessage = "Error with Name";
        ErrorMessage ??= defaultErrorMessage;
    }

    protected override ValidationResult? IsValid(object? value,
                                         ValidationContext validationContext)
    {
        if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
        {
            return new ValidationResult("Name is required.");
        }

        if (value.ToString()!.ToLower().Contains("zz"))
        {

            return new ValidationResult(
                        FormatErrorMessage(validationContext.DisplayName));
        }

        return ValidationResult.Success;
    }
}

Trong đoạn mã sau,  [ValidateName] thuộc tính tùy chỉnh được áp dụng:

public class Contact
{
    public Guid Id { get; set; }

    [ValidateName(ErrorMessage = "Name must not contain `zz`")] 
    public string? Name { get; set; }
    public string? Email { get; set; }
    public string? PhoneNumber { get; set; }
}

Khi mô hình chứa  zz, một  ValidationResult mới  sẽ được trả về.

Xác thực nút cấp cao nhất

Các nút cấp cao nhất bao gồm:

  • Thông số hành động
  • Thuộc tính điều khiển
  • Thông số xử lý trang
  • Thuộc tính mô hình trang

Các nút cấp cao nhất được ràng buộc bởi mô hình được xác thực bên cạnh việc xác thực các thuộc tính của mô hình. Trong ví dụ sau từ  ứng dụng mẫuVerifyPhone phương thức này sử dụng  RegularExpressionAttribution  để xác thực  phone tham số hành động:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Các nút cấp cao nhất có thể sử dụng  BindRequiredAttribution  với các thuộc tính xác thực. Trong ví dụ sau từ  ứng dụng mẫuCheckAge phương thức chỉ định rằng  age tham số phải được liên kết khỏi chuỗi truy vấn khi biểu mẫu được gửi:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

Trong trang Kiểm tra tuổi ( CheckAge.cshtml), có hai biểu mẫu. Biểu mẫu đầu tiên gửi  Age giá trị dưới  99 dạng tham số chuỗi truy vấn:  https://localhost:5001/Users/CheckAge?Age=99.

Khi một  age tham số được định dạng chính xác từ chuỗi truy vấn được gửi, biểu mẫu sẽ xác thực.

Biểu mẫu thứ hai trên trang Kiểm tra tuổi gửi giá  Age trị trong nội dung yêu cầu và việc xác thực không thành công. Liên kết không thành công vì  age tham số phải đến từ chuỗi truy vấn.

Lỗi tối đa

Quá trình xác thực sẽ dừng khi đạt đến số lỗi tối đa (200 theo mặc định). Bạn có thể định cấu hình số này bằng mã sau trong  Program.cs:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

builder.Services.AddSingleton
    <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();

Đệ quy tối đa

ValidationVisitor  duyệt qua biểu đồ đối tượng của mô hình đang được xác thực. Đối với các mô hình sâu hoặc có tính đệ quy vô hạn, việc xác thực có thể dẫn đến tràn ngăn xếp. MvcOptions.MaxValidationDepth  cung cấp một cách để dừng xác thực sớm nếu đệ quy của khách truy cập vượt quá độ sâu được định cấu hình. Giá trị mặc định của  MvcOptions.MaxValidationDepth là 32.

Tự động ngắn mạch

Quá trình xác thực sẽ tự động bị ngắt mạch (bỏ qua) nếu biểu đồ mô hình không yêu cầu xác thực. Các đối tượng mà thời gian chạy bỏ qua quá trình xác thực bao gồm các tập hợp nguyên thủy (chẳng hạn như  byte[]string[]Dictionary<string, string>) và các biểu đồ đối tượng phức tạp không có bất kỳ trình xác thực nào.

Xác thực phía khách hàng

Xác thực phía khách hàng sẽ ngăn việc gửi cho đến khi biểu mẫu hợp lệ. Nút Gửi chạy JavaScript gửi biểu mẫu hoặc hiển thị thông báo lỗi.

Xác thực phía máy khách sẽ tránh được một chuyến đi khứ hồi không cần thiết đến máy chủ khi có lỗi đầu vào trên biểu mẫu. Tập lệnh sau tham chiếu  _Layout.cshtml và  _ValidationScriptsPartial.cshtml hỗ trợ xác thực phía máy khách:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>

Tập  lệnh Xác thực Không phô trương của jQuery  là một thư viện giao diện người dùng tùy chỉnh của Microsoft được xây dựng trên  plugin Xác thực jQuery phổ biến  . Nếu không có Xác thực không phô trương của jQuery, bạn sẽ phải mã hóa logic xác thực giống nhau ở hai vị trí: một lần trong các thuộc tính xác thực phía máy chủ trên thuộc tính mô hình và sau đó một lần nữa trong các tập lệnh phía máy khách. Thay vào đó,  Trình trợ giúp thẻ  và  trình trợ giúp HTML  sử dụng thuộc tính xác thực và siêu dữ liệu loại từ thuộc tính mô hình để hiển thị  data- thuộc tính HTML 5 cho các thành phần biểu mẫu cần xác thực. Xác thực không phô trương của jQuery phân tích cú pháp data- thuộc tính và chuyển logic tới Xác thực jQuery, "sao chép" logic xác thực phía máy chủ sang máy khách một cách hiệu quả. Bạn có thể hiển thị lỗi xác thực trên máy khách bằng cách sử dụng trình trợ giúp thẻ như được hiển thị ở đây:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

Trình trợ giúp thẻ trước hiển thị HTML sau:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Lưu ý rằng các  data- thuộc tính trong đầu ra HTML tương ứng với các thuộc tính xác thực cho  Movie.ReleaseDate thuộc tính. Thuộc  data-val-required tính chứa thông báo lỗi sẽ hiển thị nếu người dùng không điền vào trường ngày phát hành. Xác thực không phô trương của jQuery chuyển giá trị này sang phương thức Xác thực jQuery  bắt buộc ()  , sau đó hiển thị thông báo đó trong phần tử <span> đi kèm.

Xác thực kiểu dữ liệu dựa trên loại .NET của thuộc tính, trừ khi thuộc tính đó bị ghi đè bởi  thuộc tính [DataType]  . Các trình duyệt có thông báo lỗi mặc định của riêng chúng, nhưng gói Xác thực Không phô trương Xác thực jQuery có thể ghi đè các thông báo đó. [DataType] các thuộc tính và lớp con như  [EmailAddress]  cho phép bạn chỉ định thông báo lỗi.

Xác nhận kín đáo

Để biết thông tin về xác thực kín đáo, hãy xem  vấn đề GitHub này .

Thêm xác thực vào biểu mẫu động

Xác thực không phô trương của jQuery chuyển logic và tham số xác thực sang Xác thực jQuery khi trang tải lần đầu tiên. Do đó, việc xác thực không hoạt động tự động trên các biểu mẫu được tạo động. Để bật xác thực, hãy yêu cầu Xác thực không phô trương của jQuery phân tích cú pháp biểu mẫu động ngay sau khi bạn tạo biểu mẫu đó. Ví dụ: đoạn mã sau thiết lập xác thực phía máy khách trên một biểu mẫu được thêm qua AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Phương thức này  $.validator.unobtrusive.parse() chấp nhận bộ chọn jQuery cho một đối số của nó. Phương thức này yêu cầu Xác thực không phô trương của jQuery phân tích các  data- thuộc tính của biểu mẫu trong bộ chọn đó. Sau đó, giá trị của các thuộc tính đó được chuyển đến plugin Xác thực jQuery.

Thêm xác thực vào điều khiển động

Phương thức này  $.validator.unobtrusive.parse() hoạt động trên toàn bộ biểu mẫu chứ không phải trên các điều khiển được tạo động riêng lẻ, chẳng hạn như  <input> và  <select/>. Để phân tích lại biểu mẫu, hãy xóa dữ liệu xác thực đã được thêm khi biểu mẫu được phân tích cú pháp trước đó, như trong ví dụ sau:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Xác thực phía khách hàng tùy chỉnh

Xác thực phía máy khách tùy chỉnh được thực hiện bằng cách tạo  data- các thuộc tính HTML hoạt động với bộ điều hợp Xác thực jQuery tùy chỉnh. Mã bộ điều hợp mẫu sau đây được viết cho các  thuộc tính [ClassicMovie] và  [ClassicMovieWithClientValidator] đã được giới thiệu trước đó trong bài viết này:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Để biết thông tin về cách viết bộ điều hợp, hãy xem  tài liệu Xác thực jQuery .

Việc sử dụng bộ chuyển đổi cho một trường nhất định được kích hoạt bởi  data- các thuộc tính:

  • Gắn cờ trường đang được xác thực ( data-val="true").
  • Xác định tên quy tắc xác thực và văn bản thông báo lỗi (ví dụ:  data-val-rulename="Error message.").
  • Cung cấp bất kỳ thông số bổ sung nào mà người xác nhận cần (ví dụ:  data-val-rulename-param1="value").

Ví dụ sau đây hiển thị  data- các thuộc tính cho  thuộc tính của ứng dụng mẫu ClassicMovie  :

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Như đã lưu ý trước đó,  Trình trợ giúp thẻ  và  trình trợ giúp HTML  sử dụng thông tin từ các thuộc tính xác thực để hiển thị  data- thuộc tính. Có hai tùy chọn để viết mã dẫn đến việc tạo  data- các thuộc tính HTML tùy chỉnh:

  • Tạo một lớp bắt nguồn từ  AttributionAdapterBase<TAttribution>  và một lớp triển khai  IValidationAttributionAdapterProvider , đồng thời đăng ký thuộc tính của bạn và bộ điều hợp của nó trong DI. Phương pháp này tuân theo  nguyên tắc trách nhiệm duy nhất  trong đó mã xác thực liên quan đến máy chủ và máy khách nằm trong các lớp riêng biệt. Bộ điều hợp cũng có ưu điểm là vì nó được đăng ký trong DI nên các dịch vụ khác trong DI đều có sẵn cho nó nếu cần.
  • Triển khai  IClientModelValidator  trong  lớp ValidationAttribution của bạn  . Phương pháp này có thể phù hợp nếu thuộc tính không thực hiện bất kỳ xác thực phía máy chủ nào và không cần bất kỳ dịch vụ nào từ DI.

AttributionAdapter để xác thực phía máy khách

Phương pháp hiển thị  data- thuộc tính trong HTML này được thuộc  ClassicMovie tính trong  ứng dụng mẫu sử dụng . Để thêm xác thực ứng dụng khách bằng cách sử dụng phương pháp này:

  1. Tạo một lớp bộ điều hợp thuộc tính cho thuộc tính xác thực tùy chỉnh. Bắt nguồn từ lớp  AttributionAdapterBase<TAttribution> . Tạo một  AddValidation phương thức thêm  data- thuộc tính vào kết quả được hiển thị, như trong ví dụ này:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(
            ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
            => Attribute.GetErrorMessage();
    }
    
  2. Tạo một lớp nhà cung cấp bộ điều hợp triển khai  IValidationAttributionAdapterProvider . Trong  phương thức GetAttributionAdapter  chuyển thuộc tính tùy chỉnh vào hàm tạo của bộ điều hợp, như trong ví dụ này:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter? GetAttributeAdapter(
            ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Đăng ký nhà cung cấp bộ điều hợp cho DI trong  Program.cs:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator để xác thực phía máy khách

Phương pháp hiển thị  data- thuộc tính trong HTML này được thuộc  ClassicMovieWithClientValidator tính trong  ứng dụng mẫu sử dụng . Để thêm xác thực ứng dụng khách bằng cách sử dụng phương pháp này:

  • Trong thuộc tính xác thực tùy chỉnh, hãy triển khai  giao diện IClientModelValidator  và tạo  phương thức AddValidation  . Trong  AddValidation phương thức này, hãy thêm  data- các thuộc tính để xác thực, như trong ví dụ sau:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
            => Year = year;
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult? IsValid(
            object? value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value!).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Vô hiệu hóa xác thực phía máy khách

Đoạn mã sau vô hiệu hóa xác thực ứng dụng khách trong Trang dao cạo:

builder.Services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Các tùy chọn khác để tắt xác thực phía máy khách:

  • Nhận xét tham chiếu đến  _ValidationScriptsPartial trong tất cả  .cshtml các tập tin.
  • Xóa nội dung của  tệp Pages\Shared_ValidationScriptsPartial.cshtml  .

Cách tiếp cận trước đó sẽ không ngăn cản việc xác thực phía máy khách của Thư viện lớp dao cạo nhận dạng ASP.NET Core. Để biết thêm thông tin, hãy xem  Nhận dạng giàn giáo trong các dự án ASP.NET Core .

Chi tiết vấn đề

Chi tiết sự cố  không phải là định dạng phản hồi duy nhất để mô tả lỗi API HTTP, tuy nhiên, chúng thường được sử dụng để báo cáo lỗi cho API HTTP.

Dịch vụ chi tiết sự cố triển khai  giao diện IProblemDetailsService  , hỗ trợ tạo chi tiết sự cố trong ASP.NET Core. Phương  thức mở rộng AddProblemDetails  trên  IServiceCollection  đăng ký triển khai mặc định  IProblemDetailsService .

Trong ứng dụng ASP.NET Core, phần mềm trung gian sau đây tạo ra các phản hồi HTTP chi tiết về sự cố khi  AddProblemDetails  được gọi, ngoại trừ khi  Accept tiêu đề HTTP yêu cầu  không bao gồm một trong các loại nội dung được hỗ trợ bởi  IProblemDetailsWriter đã đăng ký  (mặc định:  application/json):

  • ExceptionHandlerMiddleware : Tạo phản hồi chi tiết về sự cố khi trình xử lý tùy chỉnh không được xác định.
  • StatusCodePagesMiddleware : Tạo phản hồi chi tiết về sự cố theo mặc định.
  • DeveloperExceptionPageMiddleware : Tạo phản hồi chi tiết về sự cố trong quá trình phát triển khi  Accept tiêu đề HTTP yêu cầu không bao gồm  text/html.

Tài nguyên bổ sung

Nguồn: learn.microsoft.com
» Tiếp: Triển khai ứng dụng web ASP.NET
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!