ASP.NET Core: Model Validation trong ASP.NET Core MVC và Razor Pages
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:
-
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ệ.false
,undefined
, hoặcnull
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); }
-
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ẫu , VerifyPhone
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ẫu , CheckAge
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:
-
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êmdata-
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(); }
-
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); } }
-
Đă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êmdata-
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ồmtext/html
.