Exercise 4: Route Groups & Filters
Objective
Use ASP.NET Core 7+ features: Route Groups and Endpoint Filters.
Route Groups Implementation
var api = app.MapGroup("/api")
.WithOpenApi();
var products = api.MapGroup("/products")
.WithTags("Products")
.AddEndpointFilter(async (context, next) =>
{
// Logging filter for all product endpoints
Console.WriteLine($"Product endpoint called: {context.HttpContext.Request.Path}");
return await next(context);
});
products.MapGet("/", GetAllProducts);
products.MapGet("/{id:int}", GetProduct);
products.MapPost("/", CreateProduct);
products.MapPut("/{id:int}", UpdateProduct);
products.MapDelete("/{id:int}", DeleteProduct);
Endpoint Filters
Validation Filter
app.MapPost("/api/products", CreateProduct)
.AddEndpointFilter(async (context, next) =>
{
var product = context.GetArgument<Product>(0);
if (string.IsNullOrWhiteSpace(product.Name))
return Results.BadRequest("Name is required");
if (product.Price <= 0)
return Results.BadRequest("Price must be positive");
return await next(context);
});
ID Validation Filter
app.MapGet("/api/products/{id:int}", GetProduct)
.AddEndpointFilter(async (context, next) =>
{
var id = context.GetArgument<int>(0);
if (id <= 0)
return Results.BadRequest("Invalid ID");
return await next(context);
});
Rate Limiting with Route Groups
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("api-limit", opt =>
{
opt.Window = TimeSpan.FromMinutes(1);
opt.PermitLimit = 10;
});
});
var rateLimitedApi = app.MapGroup("/api")
.RequireRateLimiting("api-limit");
Challenge
- Create separate route groups for public and authenticated endpoints
- Add caching filter for GET requests
- Implement audit logging filter
- Add response transformation filter
Expected Outcome
- Well-organized endpoints using route groups
- Reusable endpoint filters
- Understanding of filter execution order