MinimalAPIFilters 2.0.3
dotnet add package MinimalAPIFilters --version 2.0.3
NuGet\Install-Package MinimalAPIFilters -Version 2.0.3
<PackageReference Include="MinimalAPIFilters" Version="2.0.3" />
<PackageVersion Include="MinimalAPIFilters" Version="2.0.3" />
<PackageReference Include="MinimalAPIFilters" />
paket add MinimalAPIFilters --version 2.0.3
#r "nuget: MinimalAPIFilters, 2.0.3"
#:package MinimalAPIFilters@2.0.3
#addin nuget:?package=MinimalAPIFilters&version=2.0.3
#tool nuget:?package=MinimalAPIFilters&version=2.0.3
MinimalApiFilters
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:
⚖️ 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 | Versions 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. |
-
net10.0
- Microsoft.AspNetCore.Http (>= 2.3.10)
- Microsoft.AspNetCore.OpenApi (>= 10.0.8)
- Microsoft.Extensions.Configuration (>= 10.0.8)
- Microsoft.Extensions.Logging (>= 10.0.8)
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 |