If you’ve been using Minimal APIs for a while, you’ve probably run into this pattern:
app.MapGet("/search", (
[FromQuery] string q,
[FromQuery] int page,
[FromHeader(Name = "X-Custom-Header")] string customHeader,
[FromServices] IHelloService service
) =>
{
var results = service.SayHello(q);
return Results.Ok(new
{
Query = q,
Page = page,
CustomHeader = customHeader,
Results = results
});
});
It works perfectly fine until your endpoint starts to grow.
The moment you add more parameters (headers, query params, services, etc.), the signature quickly becomes huge and hard to read/maintain.
With .NET 9, there’s a new attribute that makes this much nicer, AsParameters.
What [AsParameters] does
The [AsParameters] attribute lets you group multiple bound parameters into a single object, while still allowing the framework to handle model binding for you.
You define a record (or record struct) that contains the parameters you’d normally bind individually, annotate each property with the corresponding binding attributes (FromQuery, FromHeader, FromServices, etc.), and then use that type as a single parameter with AsParameters.
Example
Let’s say you want to search for something and need to pass a query string, a page number, a custom header, and also inject a service.
Here’s how that looks with AsParameters:
using Microsoft.AspNetCore.Mvc;
namespace AsParametersExample;
public readonly record struct SearchParameters
{
[FromQuery(Name = "q")]
public string Query { get; init; }
[FromQuery(Name = "page")]
public int Page { get; init; }
[FromHeader(Name = "X-Custom-Header")]
public string CustomHeader { get; init; }
[FromServices]
public IHelloService Service { get; init; }
}
app.MapGet("/parameters", ([AsParameters] SearchParameters parameters) =>
{
var results = parameters.Service.SayHello(parameters.Query);
return Results.Ok(new
{
Query = parameters.Query,
Page = parameters.Page,
CustomHeader = parameters.CustomHeader,
Results = results
});
});
That’s it, no need for a long parameter list AND you still get full access to all the standard model binding features.
Why it’s better
- Cleaner signatures – no more unreadable lists of 10+ bound parameters.
- Encapsulation – group related parameters logically (e.g., pagination, filtering, sorting).
- Reusability – use the same
recordacross multiple endpoints. - Testability – you can create a
SearchParametersinstance in tests without dealing with query strings or headers.
Wrapping up
This attribute is one of those small additions that make Minimal APIs feel more natural. In the example I used a readonly record struct with get; init;. If you want to avoid extra heap allocations, do it this way 🙂
Here’s the full Microsoft documentation:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding
Affiliate promo
If you love learning new stuff and want to support me, consider buying a course from Dometrain using this link: Browse courses – Dometrain. Thank you!
Leave a comment