Validation Pattern¶
Request validation using FluentValidation.
Overview¶
All incoming requests are validated before reaching the handler:
Validator Structure¶
public sealed class CreateMarkerRequestValidator
: AbstractValidator<CreateMarkerRequest>
{
public CreateMarkerRequestValidator()
{
RuleFor(x => x.Title)
.NotEmpty()
.MaximumLength(200);
RuleFor(x => x.Latitude)
.InclusiveBetween(-90, 90);
RuleFor(x => x.Longitude)
.InclusiveBetween(-180, 180);
RuleFor(x => x.Rating)
.InclusiveBetween(1, 5)
.When(x => x.Rating.HasValue);
}
}
Common Rules¶
| Rule | Use For |
|---|---|
NotEmpty() | Required strings, collections |
NotNull() | Required reference types |
MaximumLength(n) | String length limits |
InclusiveBetween(a, b) | Numeric ranges |
EmailAddress() | Email format |
Must(predicate) | Custom validation |
Conditional Validation¶
Custom Rules¶
RuleFor(x => x.CategoryId)
.MustAsync(async (id, ct) =>
await context.Categories.AnyAsync(c => c.CategoryId == id, ct))
.WithMessage("Category does not exist");
Error Response¶
Validation errors return 400 with field-specific messages:
{
"code": "VALIDATION_ERROR",
"errors": {
"title": ["Title is required"],
"latitude": ["Latitude must be between -90 and 90"]
}
}
Registration¶
Validators are auto-registered via assembly scanning:
Related¶
- CQRS — Command/Query pattern
- Error Codes — Error response format