繁体   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