DotThrow.AspNetCore 2.0.2

dotnet add package DotThrow.AspNetCore --version 2.0.2
                    
NuGet\Install-Package DotThrow.AspNetCore -Version 2.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DotThrow.AspNetCore" Version="2.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotThrow.AspNetCore" Version="2.0.2" />
                    
Directory.Packages.props
<PackageReference Include="DotThrow.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DotThrow.AspNetCore --version 2.0.2
                    
#r "nuget: DotThrow.AspNetCore, 2.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DotThrow.AspNetCore@2.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DotThrow.AspNetCore&version=2.0.2
                    
Install as a Cake Addin
#tool nuget:?package=DotThrow.AspNetCore&version=2.0.2
                    
Install as a Cake Tool

DotThrow.AspNetCore

ASP.NET Core support for DotThrow validators:

  • Minimal APIs: .WithValidation<T>() on an endpoint or a route group. Works with Native AOT and the request delegate generator.
  • MVC: a global action filter (AddDotThrowValidation()) and a [ValidateWithDotThrow] attribute.
  • ValidationReport to RFC 7807 validation problem helpers.

Targets .NET 8, .NET 9 and .NET 10. It depends on DotThrow and the ASP.NET Core shared framework. Register validators with DotThrow.DependencyInjection.

Install

dotnet add package DotThrow.AspNetCore
dotnet add package DotThrow.DependencyInjection

Packages

The DotThrow packages share one version:

  • DotThrow: guard clauses, collect mode, Validator<T>, dynamic rules, message translations and analyzers. No dependencies.
  • DotThrow.DependencyInjection: register validators in dependency injection, validate options classes, and dynamic validators from configuration.
  • DotThrow.AspNetCore (this package): validation for minimal API endpoints and MVC actions, and validation problem helpers.

Minimal APIs

using DotThrow;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidator<SignUp, SignUpValidator>();

var app = builder.Build();

app.MapPost("/sign-up", (SignUp request) => TypedResults.Ok())
    .WithValidation<SignUp>();

app.Run();

public sealed record SignUp(string? Name, string? Email);

public sealed class SignUpValidator : Validator<SignUp>
{
    public SignUpValidator()
    {
        For(x => x.Name, name => name.IfNullOrWhiteSpace());
        For(x => x.Email, email => email.IfNullOrWhiteSpace().IfNotEmailAddress());
    }
}

An invalid request never reaches the handler. The answer is a 400 application/problem+json:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Email": ["'Email' must be a valid email address."]
  }
}

The filter finds the handler parameters of type T once, when the endpoints are built. A parameter whose type derives from T is validated too. Parameters from services ([FromServices], [FromKeyedServices], or a registered service type without a binding attribute) are never validated, and an optional argument that the request did not send is skipped. When several arguments are validated, the answer has all their failures, named after the parameter ("first.Value", "second.Value").

Setup mistakes (a handler without a T parameter from the request, no IValidator<T> registered, JSON options that cannot write a validation problem) never break other endpoints: ASP.NET Core builds the endpoints at the first request, the mistake is logged as an error, and only that endpoint answers 500. To stop the app at startup instead, with every mistake listed:

builder.Services.ValidateEndpointsOnStart();

More forms:

// Groups.
app.MapPost("/orders", (Order order) => TypedResults.Ok()).WithValidation<Order>("checkout");

// A validator object, no registration needed.
app.MapPost("/notes", (Note note) => TypedResults.Ok()).WithValidation(new NoteValidator());

// Every endpoint of a group that has an Order parameter. Other endpoints are not changed.
var orders = app.MapGroup("/orders").WithValidation<Order>();

// Per-request options and a custom response.
app.MapPost("/admin/users", (CreateUser user) => TypedResults.Ok()).WithValidation<CreateUser>(o =>
{
    o.Groups = ["admin"];
    o.ConfigureValidation = (options, http) => options.Items["user"] = http.User.Identity?.Name;
    o.ResultFactory = (report, http) => report.ToValidationProblem(new ValidationProblemOptions { IncludeErrorCodes = true });
});

Validated endpoints also describe the 400 response for OpenAPI.

MVC

builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.Services.AddControllers().AddDotThrowValidation();

Every action argument whose type has an IValidator<T> registered is validated before the action runs, and each failure becomes a ModelState error named by its property path.

  • [ApiController] actions do not run: the answer is the app's usual invalid model state response, a 400 ValidationProblemDetails by default.
  • Other actions run and check ModelState.IsValid themselves, as with data annotations.

Without the global filter, put [ValidateWithDotThrow] on a controller or an action. The closest setting wins, so an attribute can change the groups or turn validation off for one action:

[ApiController]
[Route("orders")]
public sealed class OrdersController : ControllerBase
{
    [HttpPost]
    [ValidateWithDotThrow("checkout")]
    public IActionResult Create(Order order) => Ok();

    [HttpPost("draft")]
    [ValidateWithDotThrow(Skip = true)]
    public IActionResult SaveDraft(Order order) => Ok();
}

AddDotThrowValidation(o => ...) sets the default groups and a per-request ConfigureValidation.

Error codes and severities

builder.Services.Configure<ValidationProblemOptions>(o =>
{
    o.IncludeErrorCodes = true;
    o.IncludeSeverities = true;
});

Both filters then add two extensions, keyed and ordered like errors (keys follow the JSON options' DictionaryKeyPolicy, the same as errors):

"errorCodes": { "Email": ["NotEmailAddress"] },
"severities": { "Email": ["Error"] }

The value that failed (ValidationFailure.AttemptedValue) is never written to a response, because it may be a password or a token.

Helpers

var report = await validator.ValidateAsync(request);
if (!report.IsValid)
{
    return report.ToValidationProblem();           // minimal APIs: TypedResults.ValidationProblem
}

HttpValidationProblemDetails details = report.ToProblemDetails();       // minimal APIs, IProblemDetailsService
ValidationProblemDetails mvcDetails = report.ToValidationProblemDetails(); // MVC: BadRequest(mvcDetails)

Native AOT

  • .WithValidation<T>() uses no reflection per request and works with the request delegate generator (PublishAot). Register validators with AddValidator<TModel, TValidator>(), not assembly scanning.
  • Call builder.Services.AddProblemDetails(). It gives the app's JSON options the metadata that every ValidationProblem result needs in Native AOT. Without it the endpoint is a setup mistake (see above) unless you set a ResultFactory, instead of answering 500 only to invalid requests.
  • The errorCodes and severities extensions are written as JSON elements, so they need no entries in your JsonSerializerContext. When you call ToValidationProblem() or ToProblemDetails() yourself with a DictionaryKeyPolicy, pass it as keyPolicy so the keys match errors. ToValidationProblemDetails() (MVC) uses dictionaries instead, which every MVC formatter can write.
  • MVC itself does not support Native AOT, so the MVC filter does not either.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.2 67 10/11/2026
2.0.1 70 10/10/2026
2.0.0 66 10/10/2026
2.0.0-rc.1 51 10/10/2026