Please see the Explicit Parameter Binding section of Minimal APIs overview:
Binding from form values is not supported in .NET 6.
So, unfortunately, using [FromForm]
attribute and binding from forms is not supported in .NET 6 in minimal APIs.
Custom Model Binding Workaround
There is a workaround using custom model binding. This was inspired by Ben Foster's post Custom Model Binding in ASP.NET 6.0 Minimal APIs. The basic idea is to add a BindAsync
method to your type/class with the following signature:
public static ValueTask<TModel?> BindAsync(HttpContext httpContext, ParameterInfo parameter)
For your example I created a simple record
with 3 properties Id
, Name
and Status
. Then you use HttpContext.Request.Form
collection to get the required values from the Request
:
public record CreateTicketDto(int Id, string Name, string Status)
{
public static ValueTask<CreateTicketDto?> BindAsync(HttpContext httpContext, ParameterInfo parameter)
{
// parse any values required from the Request
int.TryParse(httpContext.Request.Form["Id"], out var id);
// return the CreateTicketDto
return ValueTask.FromResult<CreateTicketDto?>(
new CreateTicketDto(
id,
httpContext.Request.Form["Name"],
httpContext.Request.Form["Status"]
)
);
}
}
Now you can send data to the API using FormData without receiving an error.
Personally, I would remove the [FromForm]
attribute from the endpoint, however, in my testing it works with or without it. The above technique will work with class
types too, not just record
s.
Simpler alternative
A simpler implementation is to pass HttpContext
into the action and read all values from the ctx.Request.Form
collection. In this case your action might look something like the following:
app.MapPost("/tickets", (HttpContext ctx, IFreshdeskApiService s) =>
{
// read value from Form collection
int.TryParse(ctx.Request.Form["Id"], out var id);
var name = ctx.Request.Form["Name"];
var status = ctx.Request.Form["Status"];
var dto = new CreateTicketDto(id, name, status);
s.Add(dto);
return Results.Accepted(value: dto);
});