OrionGuard.AspNetCore 7.0.0

dotnet add package OrionGuard.AspNetCore --version 7.0.0
                    
NuGet\Install-Package OrionGuard.AspNetCore -Version 7.0.0
                    
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="OrionGuard.AspNetCore" Version="7.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OrionGuard.AspNetCore" Version="7.0.0" />
                    
Directory.Packages.props
<PackageReference Include="OrionGuard.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 OrionGuard.AspNetCore --version 7.0.0
                    
#r "nuget: OrionGuard.AspNetCore, 7.0.0"
                    
#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 OrionGuard.AspNetCore@7.0.0
                    
#: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=OrionGuard.AspNetCore&version=7.0.0
                    
Install as a Cake Addin
#tool nuget:?package=OrionGuard.AspNetCore&version=7.0.0
                    
Install as a Cake Tool

OrionGuard.AspNetCore

Turns OrionGuard validation into HTTP responses: an RFC 9457 ProblemDetails body for every OrionGuard exception, a filter that validates Minimal API and MVC requests before the handler runs, and a startup check for bound options.

dotnet add package OrionGuard.AspNetCore
using Moongazing.OrionGuard.AspNetCore.Extensions;
using Moongazing.OrionGuard.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOrionGuardAspNetCore();
builder.Services.AddValidator<CreateUserRequest, CreateUserValidator>();

var app = builder.Build();

app.UseOrionGuardValidation(); // adds app.UseExceptionHandler()

app.MapPost("/users", (CreateUserRequest request) => Results.Ok(request))
   .WithValidation<CreateUserRequest>();

app.Run();

public sealed record CreateUserRequest(string Email);

public sealed class CreateUserValidator : AbstractValidator<CreateUserRequest>
{
    public CreateUserValidator()
    {
        RuleFor(x => x.Email, nameof(CreateUserRequest.Email), p => p.NotEmpty().Email());
    }
}

POST /users with {"email":"not-an-email"} answers 422 Unprocessable Content with

{
  "type": "https://tools.ietf.org/html/rfc9457",
  "title": "Validation Failed",
  "status": 422,
  "errors": { "Email": ["..."] }
}

and the handler never runs. The core OrionGuard package comes along as a dependency.

Registration

AddOrionGuardAspNetCore(Action<OrionGuardAspNetCoreOptions>? configure = null) calls AddOrionGuard(), registers the options as a singleton, registers OrionGuardExceptionHandler through AddExceptionHandler together with AddProblemDetails(), and registers OrionGuardMvcFilter. It does not scan assemblies: register each validator yourself with AddValidator<T, TValidator>().

UseOrionGuardValidation() adds UseExceptionHandler(). If your pipeline already calls UseExceptionHandler(), the handler is picked up there and this call is redundant.

Validate a Minimal API endpoint

.WithValidation<TRequest>() adds OrionGuardEndpointFilter<TRequest>. It resolves IValidator<TRequest> from the request services, finds the first handler argument of that type, and runs ValidateAsync, so async rules run as well. A failure answers with the status the validator suggested through GuardResult.FailureWithStatus — say 409 for a conflict — and falls back to DefaultStatusCode when the validator suggested none.

Validate an MVC action

using Microsoft.AspNetCore.Mvc;
using Moongazing.OrionGuard.AspNetCore.Attributes;

public sealed record CreateUser(string Email);

[ApiController]
[Route("users")]
public sealed class UsersController : ControllerBase
{
    [HttpPost]
    [ValidateRequest]
    public IActionResult Create(CreateUser request) => Ok(request);
}

[ValidateRequest] goes on a controller or a single action and adds OrionGuardMvcFilter to that action's pipeline — there is no global filter to register.

  • For each non-null action argument it runs every IValidator<T> registered for the argument's runtime type, resolved from the request services, so scoped validators (one holding a DbContext, for instance) work.
  • Validators run one after another through ValidateAsync, so RuleForAsync rules run and two validators never share a DbContext concurrently.
  • The first invalid argument stops the action. The status is the validator's suggestion, else DefaultStatusCode.
  • Putting the attribute on both the controller and the action still validates once per request.

Map exceptions to ProblemDetails

OrionGuardExceptionHandler is an IExceptionHandler, so it needs UseOrionGuardValidation() or UseExceptionHandler() in the pipeline.

Exception Status Body
AggregateValidationException — from GuardResult.ThrowIfInvalid(), or from OrionGuard.MediatR DefaultStatusCode ValidationProblemDetails, type https://tools.ietf.org/html/rfc9457, title Validation Failed, errors keyed by parameter name
BusinessRuleValidationException BusinessRuleStatusCode ValidationProblemDetails, type https://moongazing.dev/orionguard/problems/business-rule-violation, title Business Rule Violation, errors keyed by the rule's type name
GuardException 400 ValidationProblemDetails, title Validation Failed, errors keyed by parameter name

Anything else falls through to the next handler. With UseProblemDetails = false the three bodies become { "errors": [{ "parameterName", "message" }] }, { "ruleName", "message" } and { "error", "parameterName" }; the two filters write { "<field>": ["<message>"] } instead.

OrionGuardProblemDetailsFactory.Create(...) builds the same body from a GuardResult, an AggregateValidationException or a BusinessRuleValidationException if you want to answer with it yourself.

Validate bound options at startup

using Microsoft.Extensions.DependencyInjection;
using Moongazing.OrionGuard.AspNetCore.Extensions;
using Moongazing.OrionGuard.Attributes;

public sealed class SmtpSettings
{
    [NotNull, NotEmpty] public string Host { get; set; } = default!;
    [Range(1, 65535)] public int Port { get; set; }
}

public static class OptionsSetup
{
    public static void Add(IServiceCollection services) =>
        services.AddOptions<SmtpSettings>()
            .BindConfiguration("Smtp")
            .ValidateWithOrionGuardOnStart();
}

The options are checked against their OrionGuard attributes ([NotNull], [NotEmpty], [Length], [Email], [Range], [Regex], [Positive]) and, when one is registered, an IValidator<TOptions>. ValidateWithOrionGuard() is the same check without the startup pass, so it runs on first access instead.

Health check

using Microsoft.Extensions.DependencyInjection;
using Moongazing.OrionGuard.AspNetCore.Extensions;

public static class HealthSetup
{
    public static void Add(IServiceCollection services) =>
        services.AddHealthChecks().AddOrionGuardCheck(); // name "orionguard", tags "validation", "orionguard"
}

Degraded when no IValidatorFactory is registered, Healthy otherwise. A healthy result carries ValidatorFactory (the registered factory's type name) and Version, read from the AssemblyInformationalVersionAttribute of the OrionGuard core assembly actually loaded in the process.

Options

Option Default Effect
UseProblemDetails true false makes the exception handler and both filters write plain JSON instead of ProblemDetails
DefaultStatusCode 422 Status for AggregateValidationException, and for filter failures whose validator suggests no status
BusinessRuleStatusCode 422 Status for BusinessRuleValidationException; set 400 for clients that expect it
SuppressModelStateInvalidFilter false true turns off MVC's automatic 400 for invalid model state, so OrionGuard writes every validation response

What this does not do

  • It does not find your validators. There is no assembly scanning; a type with no registered IValidator<T> passes both filters silently. If an endpoint looks unvalidated, check the registration first.
  • The Minimal API filter runs one validator per type — the last IValidator<TRequest> registered wins, because it resolves a single service. The MVC filter runs all of them. If you split rules across several validators for one type, the endpoint filter will only run one.
  • The Minimal API filter matches by argument type. If the handler takes no argument of TRequest, or the argument is null, the handler runs unvalidated.
  • With [ApiController], MVC's own model-state check runs first and answers 400 for a body that could not bind, before OrionGuard sees anything. Set SuppressModelStateInvalidFilter = true if you want one consistent error shape.
  • The exception handler maps three exception types only. Anything else is someone else's problem, by design.
  • Options validation is reflective, so it is not trimming- or NativeAOT-safe. Validate options with a hand-written IValidator<TOptions> if you publish trimmed.
  • The health check does not validate anything. It reports whether the validation stack is wired up, not whether any validator works.

Targets

net8.0, net9.0, net10.0. Uses the ASP.NET Core shared framework; no package dependencies beyond OrionGuard itself.

With the rest of OrionGuard

OrionGuard · OrionGuard.MediatR (validate the command instead of the request) · OrionGuard.Swagger (publish the same constraints in the schema) · OrionGuard.OpenTelemetry

Documentation

License

MIT. See LICENSE.txt.

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
7.0.0 83 9/20/2026
6.7.0 133 7/20/2026
6.6.2 172 6/20/2026
6.6.1 120 6/20/2026
6.6.0 142 6/19/2026
6.5.30 133 6/17/2026
6.5.29 119 6/15/2026
6.5.28 122 6/15/2026
6.5.27 116 6/15/2026
6.5.26 123 6/13/2026
6.5.25 112 6/13/2026
6.5.24 123 6/12/2026
6.5.23 126 6/12/2026
6.5.22 122 6/11/2026
6.5.21 131 6/11/2026
6.5.20 118 6/11/2026
6.5.19 115 6/11/2026
6.5.18 122 6/11/2026
6.5.16 115 6/11/2026
6.5.15 119 6/11/2026
Loading failed