aspnet-validation

v2026.09.24

ASP.NET Core validation with FluentValidation, Data Annotations, and model validation. Covers custom validators and validation pipeline. USE WHEN: user mentions "FluentValidation", "Data Annotations", "model validation", ".NET validation", "input validation", "validator", "validation pipeline" DO NOT USE FOR: Spring Validation - use `spring-validation`, Zod/Yup - use frontend validation skills

GitHub
Install command
npx skhub add claude-dev-suite/aspnet-validation
Markdown
SKILL.md

ASP.NET Validation - Quick Reference

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: aspnet-core for validation documentation.

Data Annotations

public class CreateUserRequest
{
    [Required(ErrorMessage = "Name is required")]
    [StringLength(100, MinimumLength = 2)]
    public string Name { get; set; } = default!;

    [Required]
    [EmailAddress]
    [StringLength(255)]
    public string Email { get; set; } = default!;

    [Required]
    [Range(0, 150)]
    public int Age { get; set; }

    [Url]
    public string? Website { get; set; }
}

FluentValidation Setup

// Install: dotnet add package FluentValidation.AspNetCore
builder.Services.AddValidatorsFromAssemblyContaining<Program>();

FluentValidation Patterns

public class CreateUserValidator : AbstractValidator<CreateUserRequest>
{
    private readonly IUserRepository _repository;

    public CreateUserValidator(IUserRepository repository)
    {
        _repository = repository;

        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Name is required")
            .Length(2, 100);

        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress()
            .MaximumLength(255)
            .MustAsync(BeUniqueEmail).WithMessage("Email already exists");

        RuleFor(x => x.Age)
            .InclusiveBetween(0, 150);

        RuleFor(x => x.Password)
            .NotEmpty()
            .MinimumLength(12)
            .Matches(@"[A-Z]").WithMessage("Must contain uppercase letter")
            .Matches(@"[a-z]").WithMessage("Must contain lowercase letter")
            .Matches(@"\d").WithMessage("Must contain digit")
            .Matches(@"[@$!%*?&]").WithMessage("Must contain special character");
    }

    private async Task<bool> BeUniqueEmail(string email, CancellationToken ct)
    {
        return !await _repository.ExistsAsync(u => u.Email == email);
    }
}

Validation Filter (Minimal API)

public class ValidationFilter<T> : IEndpointFilter where T : class
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        var validator = context.HttpContext.RequestServices.GetService<IValidator<T>>();
        var argument = context.Arguments.OfType<T>().FirstOrDefault();

        if (validator is not null && argument is not null)
        {
            var result = await validator.ValidateAsync(argument);
            if (!result.IsValid)
                return Results.ValidationProblem(result.ToDictionary());
        }

        return await next(context);
    }
}

Collection and Nested Validation

public class OrderValidator : AbstractValidator<CreateOrderRequest>
{
    public OrderValidator()
    {
        RuleFor(x => x.Items)
            .NotEmpty().WithMessage("Order must have at least one item");

        RuleForEach(x => x.Items).ChildRules(item =>
        {
            item.RuleFor(x => x.ProductId).GreaterThan(0);
            item.RuleFor(x => x.Quantity).InclusiveBetween(1, 1000);
        });
    }
}

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
Validation in controllersRepetitive, inconsistentUse FluentValidation + filter
Only client-side validationEasily bypassedAlways validate server-side
No async validationCan't check DB uniquenessUse MustAsync
Generic error messagesBad UXProvide specific .WithMessage()

Quick Troubleshooting

IssueLikely CauseSolution
Validator not runningNot registeredCall AddValidatorsFromAssembly
Async validator timeoutDB query slowAdd appropriate indexes
ModelState not checkedMissing [ApiController]Add attribute or check manually
Nested validation skippedMissing SetValidatorUse ChildRules or SetValidator
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/backend-frameworks/aspnet-validation

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1