Kiểm Tra Dữ Liệu (Validation) trong .NET 10

Lưu ý: Đây là bản cập nhật cho bài viết gốc của tôi.

Ngày đăng: Thứ Sáu, 05 tháng 12, 2025

Giới thiệu

Tôi đã từng viết về kiểm tra thực thể (entity validation) trong quá khứ, lý do tôi quay lại chủ đề này là vì có một số thay đổi trong .NET 10 liên quan đến validation. Chúng ta sẽ cùng tìm hiểu API validation gốc trước, sau đó giải thích những gì đã thay đổi.

Validation trong .NET

Validation được triển khai trong namespace System.ComponentModel.DataAnnotations. Tôi sẽ mô tả nó trước, sau đó chuyển sang những thay đổi nhỏ trong .NET 10.

.NET đã có cơ chế kiểm tra các lớp và thuộc tính của chúng từ rất lâu, API này được gọi là Data Annotations Validation, và nó tích hợp tốt với ASP.NET Core MVC và các framework khác (không phải Entity Framework Core, như tôi đã đề cập gần đây, cũng không phải ASP.NET Core Minimal APIs). Về cơ bản, nó dựa trên một tập hợp các attribute, một interface, và một lớp thực hiện việc kiểm tra trên một lớp mục tiêu và các thuộc tính của nó, dựa trên các attribute validation mà nó chứa.

Lớp cơ sở cho attribute validation được gọi là ValidationAttribute, và đã có sẵn một số attribute triển khai các validation phổ biến. Tôi sẽ nói về chúng ngay sau đây.

ValidationAttribute trừu tượng có hai phương thức IsValid: một phương thức nhận đối tượng ValidationContext làm tham số và trả về ValidationResult, và một phiên bản đơn giản hơn không có tham số và chỉ trả về boolean. Tại sao lại có hai phiên bản? Điều này phụ thuộc vào phiên bản nào của phương thức Validate được gọi – phiên bản có tham số ValidationContext sẽ gọi overload IsValid phù hợp. Sẽ nói thêm về điều này sau.

Cách thực hiện validation là thông qua lớp Validator, thông qua các phương thức static, và nó trả về một collection các đối tượng ValidationResult.

Validation Thông Qua Attributes

Required

Có lẽ một trong những attribute validation được sử dụng nhiều nhất là [Required]. Nó được dùng để chỉ định rằng một trường hoặc thuộc tính cụ thể là bắt buộc, nghĩa là nó phải có giá trị khác với giá trị mặc định. Đối với kiểu tham chiếu, điều này có nghĩa là nó không thể là null; đối với chuỗi, nó không thể là chuỗi rỗng hoặc chỉ chứa khoảng trắng. Các kiểu giá trị (value types) không bị ảnh hưởng bởi attribute này.

Ví dụ:

[Required(AllowEmptyStrings = false)]
public string? Name { get; set; }

Thuộc tính AllowEmptyStrings làm đúng như tên gọi: nếu được thiết lập, nó sẽ không coi là lỗi nếu chuỗi rỗng, chỉ coi là lỗi nếu chuỗi là null.

Độ Dài Chuỗi Tối Thiểu và Tối Đa

Có một số cách để định nghĩa giới hạn tối thiểu và tối đa cho một thuộc tính chuỗi: một là các attribute [MinLength][MaxLength]. Như bạn có thể tưởng tượng, chúng cho phép bạn thiết lập độ dài tối thiểu và tối đa cho một chuỗi. Lưu ý rằng chúng không làm gì khác, nghĩa là nếu chuỗi là null, chúng sẽ bị bỏ qua. Bạn có thể áp dụng một hoặc cả hai cùng lúc:

[MinLength(3)]
[MaxLength(50)]
public string? Name { get; set; }

Kích Thước Tối Thiểu và Tối Đa cho Collection hoặc Chuỗi

Có một attribute khác cũng có thể được sử dụng để định nghĩa giới hạn tối thiểu và tối đa cho một chuỗi, nhưng cũng có thể dùng cho bất kỳ collection nào: attribute [Length]. Đây là ví dụ cho một collection:

[Length(5, 10)]
public List<string> Items { get; set; } = [];

Phạm Vi Số

Cũng có thể chỉ định giới hạn dưới và/hoặc trên cho một giá trị số, thông qua attribute [Range]:

[Range(0, 10, MinimumIsExclusive = true, MaximumIsExclusive = false)]
public int Position { get; set; }

Chúng ta có thể kiểm soát việc giới hạn dưới và trên có phải là loại trừ (exclusive) hay không thông qua các thuộc tính tùy chọn MinimumIsExclusiveMaximumIsExclusive. Attribute này có thể áp dụng cho bất kỳ trường hoặc thuộc tính số nào.

Giá Trị Được Phép và Không Được Phép

Nếu chúng ta muốn trường hoặc thuộc tính của mình chỉ chấp nhận/không chấp nhận một tập hợp các giá trị định sẵn, chúng ta có thể sử dụng các attribute [AllowedValues][DeniedValues]:

[AllowedValues("Red", "Green", "Blue")]
[DeniedValues("Black", "White", "Grey")]
public string? Colour { get; set; }

Các giá trị được kiểm tra nguyên trạng, nghĩa là đối với chuỗi, không có cách nào để so sánh không phân biệt chữ hoa chữ thường. Các attribute này được giới thiệu trong .NET 8.

So Sánh Các Thành Viên

Điều gì xảy ra nếu bạn muốn so sánh giá trị của một thành viên (thuộc tính, trường) với giá trị của một thành viên khác, chẳng hạn như xác nhận mật khẩu? Hãy sử dụng attribute [Compare]:

[Required]
public string Password { get; set; }

[Compare(nameof(Password))]
public string Confirmation { get; set; }

Kiểu Dữ Liệu

Nếu chúng ta muốn nói rằng kiểu của một thuộc tính phải là một trong danh sách các kiểu đã định nghĩa, chúng ta có thể sử dụng [DataType]. Trong trường hợp này, chúng ta cần truyền một DataType hoặc một tên định nghĩa kiểu tùy chỉnh:

[DataType(DataType.Currency)]
public string Price { get; set; }

Các kiểu được phép để kiểm tra (DataType) bao gồm:

  • Custom
  • DateTime
  • Date
  • Time
  • Duration
  • PhoneNumber
  • Currency
  • Text
  • Html
  • MultilineText
  • EmailAddress
  • Password
  • Url
  • ImageUrl
  • CreditCard
  • PostalCode
  • Upload

Đối với kiểu dữ liệu Custom, thuộc tính CustomDataType phải được cung cấp.

Xin lưu ý rằng attribute này không thực sự thực hiện bất kỳ validation nào, nhưng nó được sử dụng để định nghĩa kiểu mà thành viên mục tiêu phải tuân thủ.

Giá Trị Enumeration

Đôi khi chúng ta muốn gán cho một thuộc tính chuỗi một giá trị phải khớp với một giá trị enumeration. Để làm điều đó, chúng ta có [EnumDataType], và chúng ta cần truyền cho nó kiểu enumeration mong muốn:

[EnumDataType(typeof(DayOfWeek)]
public string DayOfWeek { get; set; }

Biểu Thức Chính Quy (Regular Expressions)

Biểu thức chính quy trong .NET khá mạnh mẽ và có một cách để kiểm tra chuỗi với biểu thức chính quy bằng cách sử dụng attribute [RegularExpression]:

[RegularExpression(@"\w{5,10}")]
public string Password { get; set; }

URL

Bạn có thể sử dụng trình kiểm tra biểu thức chính quy để kiểm tra một thuộc tính có phải là URL hợp lệ hay không, nhưng một giải pháp thay thế là sử dụng attribute [Url]:

[Url]
public string ClientUrl { get; set; }

Lưu ý: attribute validator này sẽ chỉ kiểm tra xem chuỗi có bắt đầu bằng một trong các giao thức sau không, tất cả đều không phân biệt chữ hoa chữ thường:

  • http://
  • https://
  • ftp://

Địa Chỉ Email

Tương tự như URL, bạn có thể sử dụng biểu thức chính quy để kiểm tra địa chỉ email, nhưng có attribute [EmailAddress] dành chính xác cho mục đích đó:

[EmailAddress]
public string ClientEmail { get; set; }

Thẻ Tín Dụng

Để kiểm tra số thẻ tín dụng, chúng ta có [CreditCard]:

[CreditCard]
public string CreditCard { get; set; }

Attribute này sẽ kiểm tra xem thẻ có hợp lệ theo thuật toán Luhn tiêu chuẩn hay không.

Số Điện Thoại

Để kiểm tra số điện thoại, chúng ta có thể sử dụng attribute [Phone]:

[Phone]
public string ClientMobile { get; set; }

Attribute này hỗ trợ:

  • Tiền tố tùy chọn bắt đầu bằng +
  • Tập hợp các số được phân tách bằng , .
  • Phần mở rộng số tùy chọn được phân tách bằng dấu cách theo sau x/ext.

Phần Mở Rộng Tệp

Để kiểm tra xem một chuỗi có kết thúc bằng một trong các phần mở rộng tệp hay không, chúng ta có attribute [FileExtensions]:

[FileExtensions(Extensions = "gif,png,jpg,jpeg,tiff,bmp")]
public string ImageUrl { get; set; }

Thuộc tính Extensions có thể nhận danh sách các phần mở rộng tệp phân tách bằng dấu phẩy. Nếu không cung cấp phần mở rộng nào, giá trị mặc định là “png,jpg,jpeg,gif“.

Chuỗi Base64

Để kiểm tra xem một chuỗi có phải là chuỗi được mã hóa Base64 hợp lệ hay không, chúng ta có attribute [Base64String], được giới thiệu trong .NET 8:

[Base64String]
public string? ImageData { get; set; }

Custom Validation Attributes

Một tùy chọn khác là tự tạo attribute validation của riêng bạn. Nó phải kế thừa từ ValidationAttribute và triển khai phương thức IsValid, như ví dụ dưới đây:

[Serializable]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class IsEvenAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        if (IsValid(value))
        {
            return ValidationResult.Success;
        }

        return new ValidationResult($"Giá trị {value} không phải là số chẵn", new string[] { validationContext.MemberName! });
    }

    protected override bool IsValid(object? value)
    {
        if (IsNumber(value?.GetType()!))
        {
            var number = (long) Convert.ChangeType(value, TypeCode.Int64)!;
 
            if ((number % 2) == 0)
            {
                return true;
            }
        }

        return false;
    }

    private static bool IsNumber(Type? type)
    {
        if (type == null)
        {
            return false;
        }

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.SByte:
            case TypeCode.Single:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return true;
            case TypeCode.Object:
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    return IsNumber(Nullable.GetUnderlyingType(type)!);
                }
                return false;
        }

        return false;
    }
}

Trong ví dụ này, chúng ta làm cho nó chỉ áp dụng cho các thuộc tính (AttributeTargets.Property), nhưng chúng ta cũng có thể tạo một attribute áp dụng cho lớp (AttributeTargets.Class) hoặc struct (AttributeTargets.Struct). Nó kiểm tra xem giá trị mục tiêu có phải là kiểu số hay không, nếu đúng thì chuyển đổi nó thành long và kiểm tra xem nó có phải là số chẵn hay không. Lưu ý rằng bạn nên ghi đè cả hai phương thức IsValid. Đây là cách áp dụng:

[IsEven]
public long NumberOfWheels { get; set; }

Bạn cũng có thể thiết lập xem custom validation attribute của bạn có yêu cầu được gọi với ValidationContext hay không, điều này được thực hiện bằng cách trả về true hoặc false trên thuộc tính ảo RequiresValidationContext, mặc định trả về false.

Thêm Validation Attributes trong một Lớp Bên Ngoài

Một số bạn có thể biết về attribute [MetadataType]. Attribute này có thể được áp dụng cho các lớp khi chúng ta không muốn đặt trực tiếp các metadata/validator attributes vào lớp đó, hoặc cho các lớp partial, nơi ít nhất một phần được tự động tạo ra và chúng ta không thể thay đổi nó. Ví dụ:

[MetadataType(typeof(Data.DataMetadata))]
public class Data
{
    public string Name { get; set; }

    public class DataMetadata
    {
        [Required]
        public string Name { get; set; }
    }
}

Điều này có nghĩa là, thông tin cho lớp Data sẽ đến, hoàn toàn hoặc không (bạn có thể kết hợp), từ các attribute trong DataMetadata, cho các thuộc tính có tên và kiểu giống hệt nhau.

Vấn đề là: nếu chúng ta thử kiểm tra, nó sẽ không hoạt động:

var foo = new Data();
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(foo, new ValidationContext(foo), results);  //true, ngay cả khi Data.Name không được cung cấp

Tuy nhiên, có một cách để làm cho nó hoạt động: chúng ta chỉ cần thêm một metadata provider vào lớp thông qua TypeDescriptor.AddProviderTransparentAssociatedMetadataTypeTypeDescriptionProvider:

TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Data), typeof(Data.DataMetadada)), typeof(Data));

Và bây giờ nó sẽ hoạt động:

var isValid = Validator.TryValidateObject(foo, new ValidationContext(foo), results);  //false, và kết quả được điền với một ValidationResult không hợp lệ

Đây là một vấn đề đã biết, nhưng theo tôi được biết, không có kế hoạch giải quyết nó.

Nếu chúng ta muốn làm cho nó động, bằng cách xem xét một số kiểu ngẫu nhiên và kiểm tra xem nó có attribute [MetadataType] hay không:

var entityType = typeof(Data);
var attr = Attribute.GetCustomAttribute(entityType, typeof(MetadataTypeAttribute)) as MetadataTypeAttribute;
            
if (attr != null)
{
    TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(entityType, attr.MetadataClassType), entityType);
}

Tôi biết điều này không thực tế, nhưng tính đến thời điểm hiện tại, dường như đây là lựa chọn duy nhất, nếu chúng ta muốn sử dụng [MetadataType] cho các validation attributes được định nghĩa bên ngoài.

Các Loại Validation Khác

Custom Validation Sử Dụng Một Phương Thức

Và bây giờ là một điều hoàn toàn khác: điều gì xảy ra nếu bạn có một phương thức đã thực hiện validation mà bạn quan tâm? Bạn có thể sử dụng nó nếu bạn áp dụng attribute [CustomValidation]! Đây là ví dụ:

[CustomValidation(typeof(CustomValidator), nameof(NumbersValidator.IsEven))]
public long NumberOfWheels { get; set; }

Lớp chứa phương thức sẽ thực hiện validation có thể là:

  • Một lớp static
  • Một lớp public không trừu tượng với một constructor public không tham số

Đối với phương thức validation, nó cần có một trong hai chữ ký khả thi:

  • ValidationResult Validate(object entity, ValidationContext context)
  • bool Validate(object entity)

Như bạn có thể thấy, chúng khớp với các phương thức ValidateIsValid mà chúng ta đã nói đến từ đầu. Phương thức có thể là static hoặc instance, miễn là nó có thể truy cập được và không trừu tượng.

Tự Kiểm Tra Lớp (Class Self-Validation)

Điều gì xảy ra nếu bạn cần thực hiện các validation liên quan đến nhiều thuộc tính? Một tùy chọn khả thi là áp dụng một validation attribute cho lớp chứa các thuộc tính và sau đó kiểm tra giá trị đang được xác thực cho lớp thích hợp và trích xuất các thuộc tính cần kiểm tra. Một tùy chọn khác là làm cho lớp triển khai IValidatableObject! Interface này cũng là một phần của Data Annotations API và được sử dụng cho các lớp tự kiểm tra. Đây là ví dụ:

public class Contract : IValidatableObject
{
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public string? Name { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.IsNullOrWhiteSpace(Name))
        {
            yield return new ValidationResult("Thiếu Tên", new string[] { nameof(Name) });
        }

        if (StartDate == null)
        {
            yield return new ValidationResult("Thiếu Ngày Bắt Đầu", new string[] { nameof(StartDate) });
        }

        if ((EndDate != null) && (StartDate != null) && (EndDate < StartDate))
        {
            yield return new ValidationResult("Ngày Kết Thúc trước Ngày Bắt Đầu", new string[] { nameof(EndDate) });
        }
    }
}

Đây là một ví dụ đơn giản cho thấy ba validation:

  • Name không được null hoặc rỗng
  • StartDate không được null
  • Nếu cả StartDateEndDate đều được cung cấp, EndDate phải sau StartDate

Bạn có thể trả về bao nhiêu ValidationResult tùy thích, tất cả đều được tính là lỗi validation.

Thực Hiện Validation

Bây giờ, để thực sự thực hiện validation, chúng ta có một số tùy chọn:

  • Kiểm tra toàn bộ instance và tất cả các thuộc tính của nó (bao gồm tự kiểm tra lớp)
  • Kiểm tra một thuộc tính đơn lẻ

Và thêm một số tùy chọn nữa:

  • Sử dụng các attribute validation của lớp và thuộc tính có sẵn
  • Cung cấp các attribute validation của riêng chúng ta

Chúng ta cũng có thể chọn cách chúng ta muốn kết quả:

  • Ném ra một ValidationException khi lỗi validation đầu tiên được tìm thấy
  • Nhận danh sách tất cả các lỗi validation (các đối tượng ValidationResult được trả về)

Lớp helper thực hiện tất cả các thao tác validation là, không ngạc nhiên, Validator. Hãy xem cách chúng ta có thể kiểm tra toàn bộ thực thể và trả về tất cả các lỗi validation:

var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(entity, new ValidationContext(entity), results, validateAllProperties: true);

Phương thức TryValidateObject sẽ không bao giờ ném ra ngoại lệ, thay vào đó, nó sẽ điền collection results với tất cả các đối tượng ValidationResult được trả về từ tất cả các attribute validation được tìm thấy cho lớp và các thuộc tính của nó, nếu validateAllProperties được đặt là true, nếu không, nó sẽ chỉ trả về kết quả thất bại đầu tiên. TryValidateObject trả về một giá trị boolean cho biết đối tượng có hợp lệ (true) hay không (false), nhưng bạn cũng có thể xem collection kết quả: nếu nó rỗng, thì đối tượng hợp lệ.

Nếu thay vào đó chúng ta muốn kiểm tra một thuộc tính cụ thể, chúng ta sử dụng TryValidateProperty:

var isValidProperty = Validator.TryValidateProperty(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" }, results);

Ở đây, trên ValidationContext, chúng ta cần truyền tên thuộc tính đang được kiểm tra làm MemberName.

Nếu chúng ta muốn có một ValidationException được ném ra tại lỗi validation đầu tiên, chỉ cần sử dụng ValidateObject, cho toàn bộ đối tượng:

Validator.ValidateObject(entity, new ValidationContext(entity), validateAllProperties: true);

Hoặc ValidateProperty, cho một thuộc tính đơn lẻ:

Validator.ValidateProperty(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" });

Một điều khác tôi đã đề cập là, nếu bạn muốn chỉ định các attribute validation của riêng mình, bạn có thể làm điều đó bằng cách sử dụng các phương thức TryValidateValue/ValidateValue và chỉ cần truyền bất kỳ collection các attribute validation nào:

var isValid = Validator.TryValidateValue(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" }, results, new [] { new IsEvenAttribute() });
Validator.ValidateValue(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" }, new [] { new IsEvenAttribute() });

Một khía cạnh quan trọng là, các phương thức validation không thực hiện validation đệ quy, cho đến .NET 10. Điều này được nêu rõ trong tài liệu.

Validation cũng xảy ra tự động hoặc thủ công trên ứng dụng ASP.NET Core, cho các model được gửi (POST, PUT, PATCH) từ payload.

Validation Results

Một validation result chỉ là một instance của ValidationResult chứa thông báo lỗi (ErrorMessage), và tên các thành viên đã được kiểm tra (MemberNames) và được phát hiện là không hợp lệ. Có một thuộc tính static gọi là Success, có giá trị null, và chỉ đại diện cho kết quả validation không có lỗi.

Validation Contexts

Validation context là một instance của ValidationContext cần được truyền cho các phương thức TryValidateProperty/TryValidateObject/ValidateObject/ValidateProperty và chứa:

  • Đối tượng/giá trị mục tiêu cần kiểm tra (ObjectInstance)
  • Thành viên cần kiểm tra, có thể là null, cho toàn bộ đối tượng (MemberName)
  • Một collection tùy chọn các cặp khóa-giá trị để cung cấp ngữ cảnh cho validation (Items)

Bên trong các phương thức validation tùy chỉnh, chúng ta có thể sử dụng tất cả các thuộc tính này theo ý muốn.

Thông Báo Lỗi trong Validation Attributes

Một lời cuối: tất cả các attribute mặc định đều cung cấp thông báo lỗi mặc định của riêng chúng, nhưng bạn có thể ghi đè chúng theo một trong hai cách:

  • Bằng cách cung cấp một thông báo lỗi tĩnh
  • Hoặc bằng cách cung cấp tên của một kiểu và thuộc tính cung cấp thông báo lỗi tại thời điểm chạy

Đối với tùy chọn đầu tiên, thuộc tính ErrorMessage là những gì chúng ta sử dụng, đây là một ví dụ nhanh:

[Required(ErrorMessage = "Trường {0} là bắt buộc!")]
public string? Name { get; set; }

Như bạn có thể thấy, chúng ta có thể đặt các placeholder {0} trong chuỗi, và chúng sẽ được thay thế bằng tên thuộc tính thực tế. Mỗi attribute validation có thể hỗ trợ các placeholder khác, luôn là tùy chọn, ví dụ:

[StringLength(50, MinimumLength: 3, ErrorMessage = "Kích thước của trường {0} phải nằm giữa {2} và {1}")]
public string? Name { get; set; }

Đối với [StringLength], MinimumLength sẽ đi vào placeholder {2}MaximumLength vào {1}, bên cạnh tên thành viên trong {0}.

Tùy chọn khác là sử dụng các thuộc tính ErrorMessageResourceTypeErrorMessageResourceName từ ValidationAttribute. Chúng thường được sử dụng với các tệp tài nguyên, và do đó cả hai phải được thiết lập đồng thời. Một ví dụ:

[Required(ErrorMessageResourceType = typeof(Resource), ErrorMessageResourceName = "RequiredName")]
public string? Name { get; set; }

Một tùy chọn khác nữa là ghi đè phương thức FormatErrorMessage. Nó nhận tham số name là tên của thuộc tính nơi attribute validation đang được gọi (từ ValidationContext‘s MemberName), sẽ rỗng nếu đó là toàn bộ lớp, và chúng ta chỉ cần trả về một thông báo lỗi thích hợp:

public override string ReturnErrorMessage(string name) => $"Thiếu {name}!";

Thay Đổi Validation trong .NET 10

Không có gì thực sự mới về validation, nhưng có một API validation mới trong .NET 10 mang validation đến ASP.NET Core Minimal APIsBlazor. Nó có sẵn trong namespace Microsoft.Extensions.Validation, nằm trên gói NuGet Microsoft.Extensions.Validation.

Validation hiện đã được nâng cấp với hỗ trợ cho các đối tượng lồng nhau (nested objects) và collection phần tử, và là opt-in. Có hai attribute liên quan đến validation mà chúng ta sẽ nói đến ngay sau đây.

API validation này cần được đăng ký rõ ràng vào Dependency Injection bằng cách gọi AddValidation:

builder.Services.AddValidation();

Validation Attributes

Validatable Types

Attribute [ValidatableType] được sử dụng để đánh dấu một kiểu hoặc thuộc tính lồng nhau để tự động validation trên endpoint Minimal API hoặc Blazor. [SkipValidation] sau đó có thể được sử dụng để loại trừ các phần của nó:

[ValidatableType]
public class MyModel { ... }

Skip Validation

Attribute [SkipValidation] được sử dụng để yêu cầu Minimal API hoặc Blazor không kiểm tra một kiểu hoặc thành viên cụ thể:

[ValidatableType]
public class MyModel
{
    [SkipValidation]
    public SomeType SomeProperty { get; set; }
}

Thực Hiện Validation

Khi một kiểu được kích hoạt validation đến một endpoint ASP.NET Core Minimal API, validation được kích hoạt:

app.MapPost((HttpContext ctx, MyModel obj) =>
{
    //...
};

Tương tự, khi một form trong Blazor được kiểm tra, tất cả các đối tượng được ràng buộc của nó sẽ được kiểm tra, nếu được kích hoạt.

Kết Luận

Như bạn có thể thấy, Data Validations API khá mạnh mẽ, đặc biệt nếu bạn thêm tự kiểm tra lớp, sử dụng các phương thức validation tùy chỉnh và/hoặc triển khai các custom validation attributes của riêng bạn. .NET 10 đã lấp đầy một khoảng trống quan trọng, khi nói đến Minimal APIs và Blazor.

Như mọi khi, tôi rất muốn nghe ý kiến của bạn về điều này, mọi bình luận, câu hỏi, chỉnh sửa, luôn được hoan nghênh! Chúc bạn validation vui vẻ!

Bài viết được dịch và biên tập từ bài gốc của Ricardo Peres.

Chỉ mục