ASP.NET WebAPI 2 版本控制 POST with id

ASP.NET WebAPI 2 Versioning POST with id

我正在尝试在现有的高效 WebAPI 2 应用程序中实施 'Microsoft.AspNet.WebApi.Versioning' nuget,以支持新设计的 'v2' 控制器。

到目前为止,大多数控制器都能正常工作,但现在我 运行 遇到了一些 POST 控制器的问题。

示例代码:

using Microsoft.Web.Http;
using System.Web.Http;

namespace ApplicationApi.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/version")]
    public class VersionController : ApiController
    {
        //Works
        [HttpGet]
        public IHttpActionResult Get()
        {
            return Ok(true);
        }

        //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
        [HttpPost]
        public IHttpActionResult Post(int id, [FromBody]dynamic value)
        {
            return Ok(true);
        }

        //Works
        [HttpPost]
        public IHttpActionResult Post([FromBody]dynamic value)
        {
            return Ok(true);
        }
    }
}

所有 POST 方法都没有额外的 {id} 工作。但是由于我们长期以来有大量的老客户,所以我必须保持旧版本和 运行,并且不能选择删除旧控制器中的 {id}。在新控制器中不需要 {id}。

WebAPI 配置

var constraintResolver = new DefaultInlineConstraintResolver()
{
    ConstraintMap =
    {
        ["apiVersion"] = typeof( ApiVersionRouteConstraint )
    }
};
config.MapHttpAttributeRoutes(constraintResolver);

config.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
});

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

知道我做错了什么或如何解决这个问题吗?

问题是你有 2 个 post 方法,api 不知道如何 identify/generate 资源名称。

您需要做的是添加一个自定义的重复方法名的资源名。

因此,如果我们以您的示例为例,您可以将资源添加到抛出异常的资源并保留另一个资源,或者为它们提供唯一的资源名称。

例如 [HttpPost("id")].

为了确认这一点,我用你的代码做了一个测试项目,为此我使用了 swagger,但它失败了。现在,当我将 id 添加到 post 方法之一时,如下所示:

[HttpPost("id")]
public IActionResult Post(int id, [FromBody]dynamic value)
{
    return Ok(true);
}

[HttpPost]
public IActionResult Post([FromBody]dynamic value)
{
    return Ok(true);
}

然后它与以下输出一起工作:


补充主要答案 在您的代码中,您允许通过在方法

中添加 int id 来获取参数
 Post(int id, [FromBody]dynamic value)

如果你真的不想使用参数,那么你可以传递给你自定义的对象 class 并做一些类似的事情,而不是测试它:

 Post([FromBody]MyCustomObjectClass data)

还有你MyCustomerObjectClass:

class MyCustomObjectClass {
    public int? Id { get; set; }
    public dynamic Value { get; set; }
}

你不需要2个post方法,你可以只有一个,Id可以是可选的。

只是为了不让您感到困惑。我不好,我在 post 中使用了 id 作为命名约定,例如 [HttpPost("id")],它与您的方法 Id 参数无关。 id 在这种情况下只是资源的名称,它对操作没有影响。所以你可以根据你的逻辑称呼它 [HttpPost("post1")][HttpPost("post2")].

最后一个建议,使用动态测试并在短时间内启动 api 很好,但我总是使用静态类型对象。

我强烈建议您看看 this

问题与版本无关

带有 id 参数的方法不起作用的原因是您有 冲突路由:

[Route("api/version")]

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

对于名为 Version 且具有 Route("api/version") 属性的控制器,您只能具有任一属性路由, 常规路由(MVC / WebAPI 路由),但是你不能两者兼得。

在您的情况下,属性路由覆盖了 MVC 路由,因此 api/version/{id} 不再存在。但是 api/version?id 仍然有效,因为它是由属性路由寻址的。

要解决该问题,您可以将控制器上的属性路由更改为 RoutePrefix 而不是 Route,并使用 Route如有必要,在每个操作上使用相对路径。

necessary表示您有常规路由无法寻址的路由。

[ApiVersion("1.0")]
[RoutePrefix("api/version")]
public class VersionController : ApiController
{
    [HttpPost]
    public IHttpActionResult Post([FromBody] dynamic value)
    {
        return Ok();
    }

    [HttpPost]
    public IHttpActionResult Post(int id, [FromBody] dynamic value)
    {
        return Ok(1);
    }
}

尝试

[ApiVersion("1.0")]
public class VersionController : ApiController
{
    //Works
    [HttpGet]
    [Route("api/version")]
    public IHttpActionResult Get()
    {
        return Ok(true);
    }

    //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
    [HttpPost]
    [Route("api/version/{id}")]
    public IHttpActionResult Post([FromUri]int id, [FromBody]dynamic value)
    {
        return Ok(true);
    }

    //Works
    [HttpPost]
    [Route("api/version")]
    public IHttpActionResult Post([FromBody]dynamic value)
    {
        return Ok(true);
    }
}

[ApiVersion("1.0")]
[RoutePrefix("api/version/")]
public class VersionController : ApiController
{
    //Works
    [HttpGet]
    public IHttpActionResult Get()
    {
        return Ok(true);
    }

    //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
    [HttpPost]
    [Route("{id}")]
    public IHttpActionResult Post([FromUri]int id, [FromBody]dynamic value)
    {
        return Ok(true);
    }

    //Works
    [HttpPost]
    public IHttpActionResult Post([FromBody]dynamic value)
    {
        return Ok(true);
    }
}