MinimalAPIFilters 2.0.3

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

MinimalApiFilters

NuGet Version NuGet Downloads

MinimalApiFilters is a lightweight Nuget package of reusable filters for ASP.NET Core Minimal APIs. Provides plug‑and‑play validation, authorization, and logging filters to simplify endpoint pipelines.

Features

  • AuthorizationFilter: Supports authentication, role‑based access, and claim‑based access.
    Blocks unauthenticated requests with 401 Unauthorized, and rejects users without required roles or claims with 403 Forbidden.
  • ValidationFilter: Validates [Required] properties and rejects invalid payloads with clear error messages.
    Automatically enforces non‑null values for required fields.
  • LoggingFilter: Logs before and after endpoint execution, and integrates seamlessly with external structured logging libraries such as Serilog.
  • ErrorHandlingMiddleware: Catches unhandled exceptions and returns a uniform 500 Internal Server Error JSON response.
  • Built-in email validation: Automatically validates email formats without extra configuration.

Installation

Install the package from NuGet:

dotnet add package MinimalApiFilters

Usage Example


public class Order
{
    [Required] public int Id { get; set; }
    [Required] public string Product { get; set; } = string.Empty;
    [Required] public int Quantity { get; set; }

    public string? Notes { get; set; }          // optional
    public string? DiscountCode { get; set; }   // optional
    public string? Email { get; set; }          // optional, but validated if present
}

Models.cs:

// Example models used for demonstration purposes only.
// You can define your own DTOs or records according to your application's needs.

public record Order(int Id, string Product, int Quantity);
public record RegisterRequest(string Email, string Password);
public record LoginRequest(string Email, string Password);
public record ForgotPasswordRequest(string Email);
public record GetProductDto(int Id, string Name);

appsettings.json:
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Information" //Set to "Information" to allow logging of endpoint execution in LoggingFilter
    }
  },
  "AllowedHosts": "*"
}

Program.cs:

using MinimalApiFilters; 
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseHttpsRedirection();


//----------------------------------------------------------------------------------------------------
//Enable the global error middleware
app.UseGlobalErrorHandling();


//----------------------------------------------------------------------------------------------------
//ValidationFilter will automatically validate the payload and return 400 Bad Request if invalid.
// This endpoint uses ValidationFilter<Order> to enforce:
//    - The request payload must not be null
//    - All properties decorated with [Required] must be non-null
//    - Optional properties without [Required] are ignored by validation.
//    - If an Email property exists, it must match a valid email format
app.MapPost("/orders", (Order order) => Results.Ok(order))
   .AddFilter<ValidationFilter<Order>>();


//----------------------------------------------------------------------------------------------------
//AuthorizationFilter will require authentication for this endpoint, returning 401 Unauthorized if not authenticated.
app.MapGet("/activities/{id}", (int id) =>
{
    var activity = new { Id = id, Name = "Running", Date = DateTime.UtcNow };
    return Results.Ok(activity);
})
.AddFilter<AuthorizationFilter>();


//----------------------------------------------------------------------------------------------------
// Endpoint secured by requiring the user to have the "admin" role
app.MapGet("/activities/admin/{id}", (int id) =>
{
    var activity = new { Id = id, Name = "Running", Date = DateTime.UtcNow };
    return Results.Ok(activity);
})
.AddFilter(new AuthorizationFilter(requiredRoles: new[] { "admin" }));


//----------------------------------------------------------------------------------------------------
// Endpoint secured by requiring the user to have the claim "department=finance"
app.MapGet("/activities/finance/{id}", (int id) =>
{
    var activity = new { Id = id, Name = "Running", Date = DateTime.UtcNow };
    return Results.Ok(activity);
})
.AddFilter(new AuthorizationFilter(requiredClaims: new[] { ("department", "finance") }));


//----------------------------------------------------------------------------------------------------
// Endpoint secured by requiring BOTH conditions:
//    - User must have the "admin" role
//    - User must have the claim "department=finance"
app.MapGet("/activities/payments/{id}", (int id) =>
{
    var activity = new { Id = id, Name = "Running", Date = DateTime.UtcNow };
    return Results.Ok(activity);
})
.AddFilter(new AuthorizationFilter(
    requiredRoles: new[] { "admin" },
    requiredClaims: new[] { ("department", "finance") }
));


//----------------------------------------------------------------------------------------------------
//LoggingFilter will log the execution of this endpoint to the console.
app.MapPost("/logged-activities", (string name) =>
{
    return Results.Ok(new { Message = "Activity logged", Name = name });
})
.AddFilter<LoggingFilter>();


//----------------------------------------------------------------------------------------------------
//No filters applied to these endpoints, they will accept any payload and allow unauthenticated access.
app.MapPost("/register", (RegisterRequest request) =>
{
    return Results.Ok(new { Message = "User registered", request.Email });
});

app.MapPost("/login", (LoginRequest request) =>
{
    return Results.Ok(new { Token = "fake-jwt-token", request.Email });
});

app.MapPost("/forgot-password", (ForgotPasswordRequest request) =>
{
    return Results.Ok(new { Message = "Password reset link sent", request.Email });
});

app.UseAuthentication();
app.UseAuthorization();

app.Run();

Mapping Endpoint example


ProductsEndpoints.cs:

public static class ProductsEndpoints
{
    public static WebApplication MapProductsEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/products");

        // Apply AuthorizationFilter to secure the endpoint (basic authentication)
        group.MapGet("/{id}", GetProduct)
             .AddFilter<AuthorizationFilter>();

       group.MapGet("/{id}", GetProduct)
            .AddFilter(new AuthorizationFilter(requiredRoles: new[] { "admin" }));

        group.MapGet("/reports", GetReports)
             .AddFilter(new AuthorizationFilter(requiredClaims: new[] { ("department", "finance") }));

        // Apply LoggingFilter to track requests
        group.MapGet("/log/{id}", GetProduct)
             .AddFilter<LoggingFilter>();

        // Apply ValidationFilter to validate incoming payloads
        app.MapPost("/orders", (Order order) => Results.Ok(order))
           .AddFilter<ValidationFilter<Order>>();

        return app;
    }

    public static GetProductDto GetProduct(int id) => new GetProductDto(id, "Watch");

    public static object GetReports() => new { Report = "Quarterly Sales", Date = DateTime.UtcNow };
}

Program.cs:

using MinimalApiFilters; 

var app = builder.Build();

app.MapProductsEndpoints();

//...

app.Run();

Using Fluent Validation


OrderValidator.cs:

using FluentValidation;

public class OrderValidator : AbstractValidator<Order>
{
    public OrderValidator()
    {
        RuleFor(o => o.Id)
            .GreaterThan(0).WithMessage("Id must be greater than 0.");

        RuleFor(o => o.Product)
            .NotEmpty().WithMessage("Product name is required.")
            .MaximumLength(50).WithMessage("Product name must be at most 50 characters.");

        RuleFor(o => o.Quantity)
            .GreaterThan(0).WithMessage("Quantity must be greater than 0.");
    }
}

Program.cs:

using MinimalApiFilters; 
using FluentValidation;

var builder = WebApplication.CreateBuilder(args);

//Register FluentValidation validators if you want to use ValidationFilter with FluentValidation support.
builder.Services.AddValidatorsFromAssemblyContaining<OrderValidator>();
var app = builder.Build();

app.UseHttpsRedirection();


//----------------------------------------------------------------------------------------------------
//Enable the global error middleware
app.UseGlobalErrorHandling();

//ValidationFilter will automatically validate the payload and return 400 Bad Request if invalid.
app.MapPost("/orders", (Order order) => Results.Ok(order))
   .AddFilter<ValidationFilter<Order>>();

// Other endpoints...
// (AuthorizationFilter, LoggingFilter, públicos como register/login/forgot-password)

app.UseAuthentication();
app.UseAuthorization();

app.Run();

Usage Example with Serilog

The LoggingFilter is not limited to console output.
It can be connected to structured logging providers such as Serilog or Application Insights.


Program.cs:

using MinimalApiFilters;
using Serilog;

var builder = WebApplication.CreateBuilder(args);

// Configure Serilog
builder.Host.UseSerilog((ctx, cnfg) => cnfg
    .WriteTo.Console()
    .WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day));

var app = builder.Build();

app.MapPost("/logged-orders", (Order order) => Results.Ok(order))
   .AddFilter<LoggingFilter>();

//...

app.Run();

Error Messages

  • ValidationFilter

    • "Invalid request payload"
    • "Property {PropName} is required"
    • "Property Email format is invalid"
  • AuthorizationFilter

    • "401 Unauthorized"
    • "403 Forbidden"
  • LoggingFilter

    • Console log: "Executing endpoint /logged-orders"
    • Console log: "Finished endpoint /logged-orders"
  • ErrorHandlingMiddleware

    {
      "Error": "An unexpected error occurred.",
      "Details": "Object reference not set"
    }
    
    

Logging Integration Output

  • INFO: Executing endpoint /logged-orders
  • INFO: Finished endpoint /logged-orders

Logging Integration Benefits

  • Centralized monitoring --> Logs flow into Seq, Kibana, or Application Insights.
  • Performance tracking --> Measure execution time between start and finish.
  • Audit trail --> Keep a record of all endpoint calls for compliance.

MinimalApiFilters Benefits

  • Plug‑and‑play filters
    Add validation, authorization and logging to endpoints with a single line of code.

  • Role & claim authorization
    Secure endpoints by requiring specific user roles (e.g., "admin") or claims (e.g., "department=finance"), or a combination of both.

  • Email validation builtin
    Automatically rejects invalid email formats without extra configuration.

  • Consistency
    Standardized error responses (400 Bad Request, 401 Unauthorized) across endpoints.

  • Global error handling middleware
    Captures unhandled exceptions and returns a uniform 500 Internal Server Error JSON response, ensuring reliability and cleaner error management.

Quick Reference Table

Filter Purpose Error Response
ValidationFilter Validates [Required] properties and email format 400 Bad Request
AuthorizationFilter Enforces authentication, roles, and claims 401 Unauthorized / 403 Forbidden
LoggingFilter Logs before and after endpoint execution Console output
ErrorHandlingMiddleware Catches unhandled exceptions and returns uniform JSON 500 Internal Server Error

Support the Project

If you find this library useful, consider supporting its development:

Buy Me a Coffee


⚖️ License

This project is freely available under the MIT license.
You may use it without restrictions, as long as you retain the reference to the original license.

Product Compatible and additional computed target framework versions.
.NET 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.3 154 6/14/2026
2.0.2 130 5/29/2026
2.0.1 127 5/29/2026
2.0.0 129 5/29/2026
1.0.9 135 5/29/2026
1.0.8 129 5/29/2026
1.0.7 128 5/29/2026
1.0.6 134 5/29/2026
1.0.5 138 5/28/2026
1.0.4 143 5/28/2026
1.0.3 142 5/28/2026
1.0.2 128 5/28/2026
1.0.1 126 5/28/2026
1.0.0 125 5/28/2026