返回 Skill 列表
extension
分类: 开发与工程无需 API Key

aspnet-validation

使用FluentValidation、数据注解和模型验证进行ASP.NET Core验证。涵盖自定义验证器和验证管道。使用场景:用户提到“FluentValidation”、“数据注解”、“模型验证”、“.NET验证”、“输入验证”、“验证器”、“验证管道”。不适用于:Spring Validation - 请使用`spring-validation`,Zod/Yup - 请使用前端验证技能。

person作者: jakexiaohubgithub

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-Pattern | Why It's Bad | Correct Approach | |--------------|--------------|------------------| | Validation in controllers | Repetitive, inconsistent | Use FluentValidation + filter | | Only client-side validation | Easily bypassed | Always validate server-side | | No async validation | Can't check DB uniqueness | Use MustAsync | | Generic error messages | Bad UX | Provide specific .WithMessage() |

Quick Troubleshooting

| Issue | Likely Cause | Solution | |-------|--------------|----------| | Validator not running | Not registered | Call AddValidatorsFromAssembly | | Async validator timeout | DB query slow | Add appropriate indexes | | ModelState not checked | Missing [ApiController] | Add attribute or check manually | | Nested validation skipped | Missing SetValidator | Use ChildRules or SetValidator |