同一方法上的多种类型 [FromBody] .net core web api [英] Multiple types [FromBody] on same method .net core web api

查看:38
本文介绍了同一方法上的多种类型 [FromBody] .net core web api的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有一个 POST 方法的控制器,它将接收一个 xml 字符串,它可以是 2 种类型.例如:

I have a controller with one POST method, which will receive an xml string which can be of 2 types. Eg:

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

我希望能够绑定到同一路由上的多种类型 ([HttpPost("postObj")])这样我就可以使用 firstClass 接收 http://127.0.0.1:5000/api/postObj正文中的xml,或者正文中的secondClass xml,并相应地进行操作.

I would like to be able to bind to multiple types on the same route ([HttpPost("postObj")]) So that I can receive on http://127.0.0.1:5000/api/postObj with firstClass xml in the body, or secondClass xml in the body, and act accordingly.

我尝试使用相同的路线但不同的类型制作另一种方法,例如:

I tried making another method with the same route but different type like:

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

但正如预期的那样,我得到请求匹配多个操作导致歧义".

but I get "Request matched multiple actions resulting in ambiguity", as expected.

我尝试读取正文并进行检查,然后将 xml 序列化为相应的对象,但这大大降低了性能.

I tried reading the body and doing a check then serializing the xml to the respective object, but that drastically reduced the performance.

我预计每秒最多 100 个请求,使用 FromBody 绑定可以实现这一点,但手动读取正文并序列化只给我大约 15 个.

I am expecting up to 100 requests per second, and binding using FromBody is giving me that, but manually reading the body and serializing gives me only about 15.

我怎样才能做到这一点?

How can I achieve that?

推荐答案

一直在解决同样的问题,我最终得到了:

Was playing around with same issue, here is what I end up with:

我希望有以下 API:

I wish to have following API:

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

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

我还希望有单独的控制器操作,例如:

Also I wish to have separate controller actions, like:

[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) {}

因此它们可以在生成 API 文档时被 Swashbuckle 使用.

So they can be used by Swashbuckle while generating API documentation.

更重要的是我希望内置验证工作(在任何其他建议的解决方案中都不起作用).

What is even more important I wish to have built in validation working (which wont work in any other suggested solution).

为了实现这一点,我们将创建自己的操作方法选择器属性,该属性将尝试反序列化传入的请求正文,如果能够这样做,则将选择操作,否则将检查下一个操作.

To make this happen we going to create our own action method selector attribute which will try to deserialize incoming request body and if it will be able to do so then action will be chosen, otherwise next action will be checked.

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

pros: validation is working, no need for third action and/or base model, will work with swashbuckle

缺点:对于这个动作,我们读取和反序列化主体两次

cons: for this actions we are reading and deserializing body twice

注意:倒带直播很重要,否则其他人将无法读取正文

note: it is important to rewind stream, otherwise anyone else wont be able to read body

我们的控制器现在看起来像这样:

and our controller now will look like this:

[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) {}

可以在此处找到完整的示例代码

这篇关于同一方法上的多种类型 [FromBody] .net core web api的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆