簡體   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