Skip to content

Extend the ability to customize parameter binding for Minimal APIs #35489

@DamianEdwards

Description

@DamianEdwards

This issue is to discuss future updates to extend the ability to customize parameter binding for Minimal APIs, beyond what's available in .NET 6, i.e. static methods bool TryParse(string input, out T value) and ValueTask<object> BindAsync(HttpContext httpContext) on the target type.

These are some features of parameter binding to consider:

  • bind via target type (sync from request non-body sources, this is TryParse in .NET 6)
  • bind via target type (async from request inc. body, this is BindAsync in .NET 6)
  • bind via registered type (inc. overwriting built-in binders), e.g. register IParameterBinder<T> in DI
  • bind per parameter, e.g. ([Binder(typeof(CustomerBinder))]Customer customer) => { }
  • register via DI and accept services from DI
  • have access to method/parameter info (i.e. callsite details, access to the parameter is supported by BindAsync in .NET 6 now)
  • compose with other binders (e.g. composite binders)
  • emit/mutate endpoint metadata, e.g. for OpenAPI
  • AOT friendliness

Strawman

public interface IParameterBinderFactory<T>
{
    IParameterBinder<T> Create(IServiceProvider provider, ParameterInfo parameter, MethodInfo method);
}

public interface IParameterBinder<T>
{
    ValueTask<T> BindAsync(HttpContext httpContext);
}

Example usage:

var builder = WebApplication.CreateBuilder();

builder.Services.AddSingleton<IParameterBinderFactory<Customer>, CustomerBinder>();

var app = builder.Build();

app.MapPost("/customers", (Cusomter customer) =>
{
   return Results.Created(customer);
});

public class CustomerBinder : IParameterBinderFactory<Customer>, IParameterBinder<Customer>
{
    public CustomerBinder Create(IServiceProvider provider, ParameterInfo parameter, MethodInfo method)
    {
        // Called at application startup, access to parameter and method here to change behavior based on
        // naming, attributes, etc.
        return new CustomerBinder();
    }

    public async ValueTask<Customer> BindAsync(HttpContext httpContext)
    {
        // Called per invocation of routing delegate, i.e. per request
        // Do whatever custom binding logic desired, including reading from request body, etc.
        return await httpContext.Request.ReadAsJsonAsync<Customer>();
    }
}

Related issues:

Metadata

Metadata

Assignees

Labels

Needs: DesignThis issue requires design work before implementating.api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-minimal-actionsController-like actions for endpoint routingtriage-focusAdd this label to flag the issue for focus at triage

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions