## Features - **Composable validators:** Build validators by inheriting from `Validator` and defining rules with `RuleFor` and `RuleForEach`. - **Built-in and custom rules:** Use helpers like `NotEmpty`, `Length`, `Between`, and `Matches`, or define custom predicates with `Must`. - **Structured validation output:** Each failure is returned as a `Problem` with a property path, message, severity, code, and attempted value. - **Nested validation:** Reuse validators for complex object graphs with `SetValidator`, including DI-based resolution. - **Configurable paths:** Rewrite or remove rule property paths when the reported path should differ from the CLR member path. ## Getting Started ### Install the NuGet package: ```shell dotnet add package Geekeey.Request.Validation ``` You may need to add our NuGet feed to your nuget.config this can be done by running the following command: ```shell dotnet nuget add source -n geekeey https://code.geekeey.de/api/packages/geekeey/nuget/index.json ``` ### Usage ```csharp using Geekeey.Request.Validation; public sealed record Address(string? Street); public sealed record CreateUserRequest( string? Name, int Age, Address? Address, IReadOnlyList Tags); public sealed class AddressValidator : Validator
{ public AddressValidator() { RuleFor(address => address.Street) .NotEmpty(); } } public sealed class CreateUserRequestValidator : Validator { public CreateUserRequestValidator() { RuleFor(request => request.Name) .NotEmpty() .Length(2, 100) .WithCode("NAME_INVALID"); RuleFor(request => request.Age) .Between(18, 120); RuleFor(request => request.Address) .SetValidator(new AddressValidator()); RuleForEach(request => request.Tags) .NotEmpty() .WithSeverity(Severity.Warning); } } public sealed record LoginInput(string? Username); public sealed record LoginRequest(LoginInput Input); public sealed class LoginInputValidator : Validator { public LoginInputValidator() { RuleFor(input => input.Username) .NotEmpty(); } } public sealed class LoginRequestValidator : Validator { public LoginRequestValidator() { RuleFor(request => request.Input) .WithoutPropertyPath() .SetValidator(new LoginInputValidator()); } } var validator = new CreateUserRequestValidator(); var validation = validator.Validate(new CreateUserRequest( Name: "", Age: 16, Address: new Address(""), Tags: ["", "admin"])); foreach (var problem in validation.Problems) { Console.WriteLine($"{problem.PropertyPath}: {problem.Message}"); } ``` The resulting `Problem` entries include full property paths like `Address.Street` and `Tags[0]`, making it easy to surface validation errors back to callers or APIs. If the validation path needs to differ from the CLR property path, use `WithPropertyPath(...)` for a custom transform or `WithoutPropertyPath()` to drop the current rule path entirely before nested paths are appended.