-
Notifications
You must be signed in to change notification settings - Fork 0
Minimal APIs
CacheWeave supports ASP.NET Core Minimal APIs via CacheWeaveEndpointFilter, an IEndpointFilter, and the WithCacheWeave() / WithCacheWeaveEvict() extension methods on RouteHandlerBuilder.
No additional registration is required beyond AddCacheWeave:
builder.Services.AddCacheWeave(options => { ... });Chain WithCacheWeave(...) onto any route handler to cache its response:
app.MapGet("/api/products", async (IMediator mediator, [AsParameters] GetProductsQuery query) =>
{
var result = await mediator.Send(query);
return Results.Ok(result);
})
.WithCacheWeave("products:list", expirySeconds: 300, includeQueryParams: true);// Explicit key
.WithCacheWeave("products:list", expirySeconds: 300)
// Derived key (resolves to endpoint display name)
.WithCacheWeave()
// Full options
.WithCacheWeave(new CacheWeaveAttribute("products:list")
{
ExpirySeconds = 300,
IncludeRouteParams = true,
IncludeQueryParams = true,
NoCacheWhen = NoCacheCondition.OnErrorOrEmpty,
SlidingExpiry = false
})Route parameters (path segments like {id}) are automatically included in the cache key by default. Framework route values (controller, action, page, area) are always excluded.
// GET /api/products/42 → key: "products:id=42"
app.MapGet("/api/products/{id}", async (IMediator mediator, Guid id) =>
{
var product = await mediator.Send(new GetProductByIdQuery(id));
return product is null ? Results.NotFound() : Results.Ok(product);
})
.WithCacheWeave("products", expirySeconds: 600);
// Exclude specific route params
// GET /api/products/42/draft → key: "products:id=42" (version excluded)
app.MapGet("/api/products/{id}/{version}", async (int id, string version) => ...)
.WithCacheWeave("products", excludeRouteParams: ["version"]);
// Disable route params entirely
app.MapGet("/api/products/{id}/stats", async (int id) => ...)
.WithCacheWeave("products:stats", includeRouteParams: false);
// Route params + query params combined
// GET /api/products/42/reviews?page=2 → key: "products:reviews:id=42:page=2"
app.MapGet("/api/products/{id}/reviews", async (int id, int page) => ...)
.WithCacheWeave("products:reviews");Chain WithCacheWeaveEvict(...) onto mutation routes:
app.MapPost("/api/products", async (IMediator mediator, CreateProductCommand cmd) =>
{
var id = await mediator.Send(cmd);
return Results.Created($"/api/products/{id}", id);
})
.WithCacheWeaveEvict(prefix: "products:");// Evict by exact key
.WithCacheWeaveEvict(key: "products:list")
// Evict by prefix
.WithCacheWeaveEvict(prefix: "products:")
// Evict on failure too
.WithCacheWeaveEvict(prefix: "products:", evictOnFailure: true)var products = app.MapGroup("/api/products").WithTags("Products");
products.MapGet("/", async (IMediator mediator, [AsParameters] GetProductsQuery query) =>
Results.Ok(await mediator.Send(query)))
.WithCacheWeave("products:list", expirySeconds: 300, includeQueryParams: true);
// Route param {id} is automatically included in the key
// GET /api/products/abc-123 → key: "products:detail:id=abc-123"
products.MapGet("/{id:guid}", async (IMediator mediator, Guid id) =>
{
var product = await mediator.Send(new GetProductByIdQuery(id));
return product is null ? Results.NotFound() : Results.Ok(product);
})
.WithCacheWeave("products:detail", expirySeconds: 600);
products.MapPost("/", async (IMediator mediator, CreateProductCommand cmd) =>
{
var id = await mediator.Send(cmd);
return Results.Created($"/api/products/{id}", id);
})
.WithCacheWeaveEvict(prefix: "products:");
products.MapPut("/{id:guid}", async (IMediator mediator, Guid id, UpdateProductCommand cmd) =>
{
await mediator.Send(cmd with { Id = id });
return Results.NoContent();
})
.WithCacheWeaveEvict(prefix: "products:");
products.MapDelete("/{id:guid}", async (IMediator mediator, Guid id) =>
{
await mediator.Send(new DeleteProductCommand(id));
return Results.NoContent();
})
.WithCacheWeaveEvict(prefix: "products:");app.MapPost("/api/products/search", async (IMediator mediator, SearchProductsQuery query) =>
Results.Ok(await mediator.Send(query)))
.WithCacheWeave(new CacheWeaveAttribute("products:search")
{
HashBody = true,
HashBodyFields = ["Term", "CategoryId", "Filters"],
ExpirySeconds = 120
});WithCacheWeave() adds CacheWeaveEndpointFilter to the endpoint's filter pipeline. It composes with other filters in declaration order:
app.MapGet("/api/products", handler)
.RequireAuthorization() // auth filter runs first
.WithCacheWeave("products:list") // cache filter runs second
.WithOpenApi();When no key is provided, CacheWeave falls back to ActionDescriptor.DisplayName (the endpoint's display name, typically the route pattern). For predictability, always provide an explicit key in Minimal APIs.
-
[CacheWeave]and[CacheWeaveEvict]attributes on lambda handlers are not read — useWithCacheWeave()andWithCacheWeaveEvict()instead. - Attribute decoration works only on controller actions and Razor Page handlers.