.NET 6极简API和multipart/form-data

9
使用.NET 6 Minimal API,我正在尝试在POST方法中处理multipart/form-data。但是,以下代码无法实现:
app.MapPost("/tickets", async (IFreshdeskApiService s, [FromForm] CreateTicketDto dto) => await s.Add(dto))
   .Accepts<CreateTicketDto>("multipart/form-data");

我收到了带有以下请求体的400错误请求:

{
    "error": "Expected a supported JSON media type but got \"multipart/form-data; boundary=--------------------------391539519671819893009831\"."
}

我已经切换到非最小API(使用app.MapControllers()),但在最小API中有没有处理此问题的方法?

1个回答

18
请参考ASP.NET Core 6.0 中的 Minimal APIs 概述中的“显式参数绑定”部分:

.NET 6 不支持从表单值绑定。

因此,在 ASP.NET Core 6 的 minimal APIs 中,使用 [FromForm] 属性和从表单绑定不被支持。


自定义模型绑定解决方法

可以使用自定义模型绑定来解决这个问题。这受到 Ben Foster 的文章《ASP.NET 6.0 Minimal APIs 中的自定义模型绑定》的启发。基本思路是向你的类型/类添加一个具有以下签名的BindAsync方法:

public static ValueTask<TModel?> BindAsync(HttpContext httpContext, ParameterInfo parameter)

针对你的示例,我创建了一个简单的记录,其中包含3个属性:IdNameStatus。然后您可以使用HttpContext.Request.Form集合从请求(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"]
            )
        );
    }
}
现在你可以使用FormData向API发送数据而不会收到错误。 Postman中显示成功的图片 个人建议从端点中删除[FromForm]属性。然而,在我的测试中,带或不带这个属性都可以工作。上述技术也适用于类类型,而不仅限于记录类型。
更简单的替代方法HttpContext传递给操作,并从ctx.Request.Form集合中读取所有值,是一种更简单的实现方式。在这种情况下,您的操作可能如下所示:
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);
});

1
非常好,感谢您提供了另一种选择。它似乎不再是最小的,特别是处理多个字段时,但很高兴它能够工作。 - Dariusz Woźniak
4
有一个开放的 GitHub 问题:https://github.com/dotnet/aspnetcore/issues/39430,因此在 .NET 7 中可能会重新添加对 [FromForm] 的支持。 - haldo
从.NET 8预览版6开始,现在已经支持此功能。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接