.NET 6 Minimal API and multipart/form-data
.net-6.0, asp.net-core, minimal-apis, multipartform-data, rest
Solution
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);
});
Problem
Using the .NET 6 Minimal API, I'm trying to handle `multipart/form-data` in the POST method. However, with the following code: ``` app.MapPost("/tickets", async (IFreshdeskApiService s, [FromForm] CreateTicketDto dto) => await s.Add(dto)) .Accepts<CreateTicketDto>("multipart/form-data"); ``` I'm receiving 400 Bad Request with body: ``` { "error": "Expected a supported JSON media type but got \"multipart/form-data; boundary=--------------------------391539519671819893009831\"." } ``` I switched to the non-minimal API (i.e. using `app.MapControllers()`), but is there any way to handle this in minimal API?