相同方法.NET Core Web API上的多种类型[FromBody]

Tar*_*rek 5 c# xml .net-core asp.net-core asp.net-core-webapi

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

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

我希望能够在同一路由上绑定到多个类型([HttpPost(“ postObj”)]),以便可以在正文中具有firstClass xml的http://127.0.0.1:5000/api/postObj上接收,或正文中的secondClass xml,并采取相应措施。

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

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

但正如预期的那样,我收到“请求匹配多个操作导致歧义”。

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

我期望每秒最多100个请求,并且使用FromBody进行绑定可以为我提供这些信息,但是手动读取正文并进行序列化仅给我约15个请求。

我该如何实现?

mac*_*mac 7

正在解决同样的问题,这就是我最终得到的结果:

我希望有以下 API:

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

PATCH /persons/1
{"age": 33}
Run Code Online (Sandbox Code Playgroud)

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

[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) {}
Run Code Online (Sandbox Code Playgroud)

因此,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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

优点:验证有效,不需要第三个操作和/或基本模型,可以与 swashbuckle 一起使用

缺点:对于此操作,我们正在读取和反序列化 body 两次

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

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

[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) {}
Run Code Online (Sandbox Code Playgroud)

完整的示例代码可以在这里找到


luc*_*cky 3

您不能定义具有相同路线的两个操作。操作选择器不考虑它们的参数类型。那么,为什么不合并这些动作呢?

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; }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在这种情况下,您必须以不同的方式发布对象。因此,如果您之前将 `firstClass` 数据发布为 JSON,现在您必须将其发布为 `{"firstClass": {}}`。 (3认同)