簡體   English   中英

多個類型[FromBody]在同一方法.net核心web api上

[英]Multiple types [FromBody] on same method .net core web api

我有一個控制器有一個POST方法,它將接收一個xml字符串,可以是2種類型。 例如:

[HttpPost("postObj")]
    public async Task<IActionResult> postObj([FromBody]firstClass data)
    {
        if (data != null)...

我希望能夠綁定到同一路徑上的多個類型([HttpPost(“postObj”)])這樣我就可以在http://127.0.0.1:5000/api/postObj上接收到身體中的firstClass xml ,或者身體中的secondClass xml,並相應地采取行動。

我嘗試使用相同的路線制作另一種方法,但不同的類型如:

    [HttpPost("postObj")]
    public async Task<IActionResult> postObj([FromBody]secondClass data)
    {
        if (data != null)...

但正如預期的那樣,我得到“請求匹配的多個操作導致模糊”。

我嘗試讀取正文並進行檢查,然后將xml序列化到相應的對象,但這大大降低了性能。

我期待每秒最多100個請求,使用FromBody的綁定給了我這個,但手動閱讀正文和序列化只給我15個。

我怎樣才能做到這一點?

您無法使用相同路徑定義兩個操作。 操作選擇器不考慮其參數類型。 那么,你為什么不合並這些行動;

public async Task<IActionResult> postObj([FromBody]EntireData data)
{
    if (data.FirstClass != null)
    {
        //Do something
    }
    if (data.SecondClass != null)
    {
        //Do something
    }
}

public class EntireData
{
    public FirstClass  firstClass { get; set; }

    public SecondClass secondClass { get; set; }
}

正在玩同樣的問題,這是我最終得到的:

我希望有以下API:

PATCH /persons/1
{"name": "Alex"}

PATCH /persons/1
{"age": 33}

我也希望有單獨的控制器操作,例如:

[HttpPatch]
[Route("person/{id:int:min(1)}")]
public void PatchPersonName(int id, [FromBody]PatchPersonName model) {}

[HttpPatch]
[Route("person/{id:int:min(1)}")]
public void PatchPersonAge(int id, [FromBody]PatchPersonAge model) {}

因此,Swashbuckle可以在生成API文檔時使用它們。

更重要的是,我希望內置驗證工作(在任何其他建議的解決方案中都不起作用)。

為了實現這一點,我們將創建自己的操作方法選擇器屬性,該屬性將嘗試反序列化傳入的請求體,如果能夠這樣做,則將選擇操作,否則將檢查下一個操作。

public class PatchForAttribute : ActionMethodSelectorAttribute
{
    public Type Type { get; }

    public PatchForAttribute(Type type)
    {
        Type = type;
    }

    public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
    {
        routeContext.HttpContext.Request.EnableRewind();
        var body = new StreamReader(routeContext.HttpContext.Request.Body).ReadToEnd();
        try
        {
            JsonConvert.DeserializeObject(body, Type, new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error });
            return true;
        }
        catch (Exception)
        {
            return false;
        }
        finally
        {
            routeContext.HttpContext.Request.Body.Position = 0;
        }
    }
}

專業人士 :驗證工作正常,不需要第三個動作和/或基礎模型,可以使用swashbuckle

缺點 :對於這個動作,我們正在閱讀和反序體化兩次

注意 :重繞流是很重要的,否則其他任何人都無法讀取身體

我們的控制器現在看起來像這樣:

[HttpPatch]
[Route("person/{id:int:min(1)}")]
[PatchFor(typeof(PatchPersonName))]
public void PatchPersonName(int id, [FromBody]PatchPersonName model) {}

[HttpPatch]
[Route("person/{id:int:min(1)}")]
[PatchFor(typeof(PatchPersonAge))]
public void PatchPersonAge(int id, [FromBody]PatchPersonAge model) {}

可以在此處找到完整的示例代碼

作為免責聲明,我認為這有點像hacky。 我推動將發送給您的對象更改為一個對象,該對象可以表示兩種情況,或者將兩種不同的對象類型發布到兩個不同的URI。

有了這個,只需讓它工作就可以通過以下教程創建自定義IModelBinder: https//dotnetcoretutorials.com/2016/12/28/custom-model-binders-asp-net-核心/

這里的關鍵是你綁定的模型將是一個基類,它有兩個不同的派生類。

  • 您的自定義模型綁定器將根據輸入數據選擇要創建的綁定器並執行此操作。
  • 您的控制器操作將作為基本類型的輸入,並配置為使用您的自定義綁定器。
  • 您的操作需要檢查傳入的對象的實際類型並相應地處理它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM