validation-patterns
Comprehensive validation patterns for ASP.NET Core applications. Covers FluentValidation integration, DataAnnotations, IValidatableObject, IValidateOptions<T>, MediatR pipeline behavior, and client-side validation. Use when implementing validation in ASP.NET Core applications, setting up FluentValid
By wshaddix · 526 installs
npx skills add wshaddix/dotnet-skills --skill validation-patterns
Source repository · Upstream listing
Validation Patterns in ASP.NET Core
Rationale
Validation is critical for both security and user experience. Poor validation leads to invalid data, security vulnerabilities, and confusing error messages. These patterns provide a comprehensive approach to validation at multiple layers.
Validation Strategy
Layer Purpose Technology
Client Side Immediate feedback, reduce server load jQuery Validation, HTML5
Model Binding Data type/format validation Model Binders
Application Business rule validation FluentValidation, DataAnnotations
Configuration Startup validation IValidateOptions<T
Database Constraint enforcement EF Core Configurations
Validation Approach Decision Tree
Choose the validation approach based on complexity:
1. DataAnnotations (default) declarative [Required] , [Range] , [StringLength] , [RegularExpression] attributes. Best for simple property level constraints.
2. IValidatableObject implement Validate() for cross property rules. Best for date range comparisons, conditional required fields.
3. Custom ValidationAttribute subclass ValidationAttribute for reusable property level rules.
4. IValidateOptions<T validate configuration/options classes at startup with access to DI services.
5. FluentValidation third party library for complex, testable validation with fluent API. Best for async validators, database dependent rules.
Pattern 1: DataAnnotations
The System.ComponentModel.DataAnnotations namespace provides declarative validation through attributes.
Attribute Reference
Attribute Purpose Example
[Required] Non null, non empty [Required]
[StringLength] Min/max length [StringLength(200, MinimumLength = 1)]
[Range] Numeric/date range [Range(1, 100)]
[RegularExpression] Pattern match [RegularExpression(@"^\d{5}$")]
[EmailAddress] Email format [EmailAddress]
[Phone] Phone format [Phone]
[Url] URL format [Url]
[CreditCard] Luhn check [CreditCard]
[Compare] Property equality [Compare(nameof(Password))]
[MaxLength] / [MinLength] Collection/string length [MaxLength(50)]
[AllowedValues] (.NET 8+) Value allowlist [AllowedValues("Draft", "Published")]
[DeniedValues] (.NET 8+) Value denylist [DeniedValues("Admin", "Root")]
[Length] (.NET 8+) Min and max in one [Length(1, 200)]
[Base64String] (.NET 8+) Base64 format [Base64String]
Pattern 2: Custom ValidationAttribute
Create reusable validation attributes for domain specific rules.
Property Level
Class Level
Pattern 3: IValidatableObject
Implement IValidatableObject for cross property validation within the model:
When to use IValidatableObject vs custom attribute: Use IValidatableObject when validation logic is specific to one model. Use custom ValidationAttribute when the same rule applies across multiple models.
Pattern 4: IValidateOptions<T
Validate configuration/options classes at startup with access to DI services:
Registration
Pattern 5: FluentValidation Setup
NuGet Packages
Configuration
Basic Validator
Conditional Validation
Collection Validation
Pattern 6: MediatR Validation Pipeline Behavior
Registration
Pattern 7: Manual Validation
Run DataAnnotations validation programmatically:
Critical: Without validateAllProperties: true , Validator.TryValidateObject only checks [Required] attributes.
Pattern 8: Validating File Uploads
Anti Patterns
Duplicate Validation
Silent Validation Failures
Trusting Client Side Validation
Agent Gotchas
1. Always pass validateAllProperties: true to Validator.TryValidateObject .
2. Options classes must use { get; set; } not { get; init; } configuration binder needs to mutate properties.
3. IValidatableObject.Validate() runs only after all attribute validations pass do not rely on it for primary validation.
4. Do not inject services into ValidationAttribute via constructor use validationContext.GetService<T () inside IsValid() .
5. Register IValidateOptions<T as singleton the options validation infrastructure resolves validators as singletons.
6. Do not forget ValidateOnStart() without it, options validation only runs on first access.
References
[FluentValidation](https://docs.fluentvalidation.net/)
[Model Validation in ASP.NET Core](https://learn.microsoft.com/en us/aspnet/core/mvc/models/validation)
[Data Annotations](https://learn.microsoft.com/en us/dotnet/api/system.componentmodel.dataannotations)
[IValidateOptions](https://learn.microsoft.com/en us/dotnet/core/extensions/options options validation)
[jQuery Validation](https://jqueryvalidation.org/)