繁体   English   中英

如何在 ASP.NET Core 中结合 FromBody 和 FromForm BindingSource?

[英]How to combine FromBody and FromForm BindingSource in ASP.NET Core?

我创建了一个新的 ASP.NET Core 2.1 API 项目,带有一个Data dto 类和这个控制器操作:

[HttpPost]
public ActionResult<Data> Post([FromForm][FromBody] Data data)
{
    return new ActionResult<Data>(data);
}
public class Data
{
    public string Id { get; set; }
    public string Txt { get; set; }
}

它应该将数据回显给用户,没什么特别的。 但是,这两个属性中只有一个有效,具体取决于顺序。

以下是测试请求:

curl -X POST http://localhost:5000/api/values \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'id=qwer567&txt=text%20from%20x-www-form-urlencoded'

curl -X POST http://localhost:5000/api/values \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "abc123",
    "txt": "text from application/json"
}'

我尝试了几种方法,都无济于事:

  • 创建自定义子BindingSource ,但这似乎只是元数据。
  • 使用属性[CompositeBindingSource(...)] ,但构造函数是私有的,这可能不是预期用途
  • 为此创建IModelBinder和提供程序,但是 (1) 我可能只希望在特定的控制器操作上使用它,并且 (2) 获得两个内部模型绑定器(用于 Body 和 FormCollection)似乎确实需要做很多工作

那么,将FromFormFromBody (或者我猜任何其他来源的组合)属性合并为一个的正确方法是什么?

澄清这背后的原因,并解释为什么我的问题不是这个问题的重复:我想知道如何使用相同的 URI/路由来支持两种不同类型的发送数据。 (尽管可能对某些人的口味,包括我自己的口味,不同的路线/uri 可能更合适。)

您可以使用自定义IActionConstraint实现您正在寻找的IActionConstraint

从概念上讲,IActionConstraint 是一种重载形式,但它不是重载具有相同名称的方法,而是在匹配相同 URL 的操作之间进行重载。

我对此进行了一些尝试,并提出了以下IActionConstraint实现:

public class FormContentTypeAttribute : Attribute, IActionConstraint
{
    public int Order => 0;

    public bool Accept(ActionConstraintContext ctx) =>
        ctx.RouteContext.HttpContext.Request.HasFormContentType;
}

如您所见,它非常简单——它只是检查传入的 HTTP 请求是否属于表单内容类型。 为了使用它,您可以归因于相关操作。 这是一个完整的示例,其中还包含此答案中建议的想法,但使用您的操作:

[HttpPost]
[FormContentType]
public ActionResult<Data> PostFromForm([FromForm] Data data) =>
    DoPost(data);

[HttpPost]
public ActionResult<Data> PostFromBody([FromBody] Data data) =>
    DoPost(data);

private ActionResult<Data> DoPost(Data data) =>
    new ActionResult<Data>(data);

[FromBody]在上面是可选的,因为使用了[ApiController] ,但我已经在示例中明确地包含了它。

也来自文档:

...带有 IActionConstraint 的动作总是被认为比没有的动作更好。

这意味着当传入的请求不是表单内容类型时,我展示的FormContentType属性将排除该特定操作,因此使用PostFromBody 否则,如果它表单内容类型,则PostFromForm操作将获胜,因为它被“认为更好”。

我已经在相当基本的水平上对此进行了测试,它似乎确实可以满足您的需求。 可能有些情况下它不太合适,所以我鼓励你玩玩它,看看你可以用它去哪里。 我完全希望您可能会发现它完全倒塌的情况,但仍然是一个值得探索的有趣想法。

最后,如果您不想使用属性,可以配置一个约定,例如使用反射来查找具有[FromForm]属性的操作并自动添加约束。 这篇关于该主题的优秀文章中有更多详细信息。

你不能。 一个动作只能接受一个或另一个。 为了解决这个问题,您可以简单地创建多个动作,一个有[FromBody] ,一个没有。 它们当然也需要单独的路由,因为属性的存在不足以区分重载。 但是,您可以将 action 的主体分解为两个 action 都可以使用的私有方法,至少可以保持 DRY。

nuget 包 Toycloud.AspNetCore.Mvc.ModelBinding.BodyAndFormBinding 应该适合您的需求

github

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM