1

我正在尝试在现有的生产性 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);
        }
    }
}

所有没有附加 {id} 的 POST 方法都有效。但是由于我们有大量的旧客户端很长一段时间,我必须保持旧版本正常运行,并且在旧控制器中删除 {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 }
);

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

4

3 回答 3

3

该问题与版本控制无关。

带有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在必要时在每个操作上使用相对路径。

必要的意味着您有无法通过常规路由解决的路由。

[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);
    }
}
于 2020-03-21T03:00:58.200 回答
1

问题是您有 2 个 post 方法,而 api 不知道如何识别/生成资源名称。

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

因此,如果我们以您的示例为例,您可以将资源添加到引发异常的那个并保留另一个,或者给它们两个唯一的资源名称。

例如像[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)

如果您真的不想使用参数,那么您可以传递您自定义的对象类并执行类似的操作,而无需对其进行测试:

 Post([FromBody]MyCustomObjectClass data)

MyCustomerObjectClass

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

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

只是为了不让你感到困惑。我在帖子中使用 id 作为命名约定是我的错[HttpPost("id")],它与您的方法 Id 参数无关。id在这种情况下只是资源的名称,它对动作没有影响。所以你可以称它为任何符合你的逻辑[HttpPost("post1")][HttpPost("post2")].

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

我强烈建议看看这个

于 2020-03-19T07:11:57.147 回答
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);
    }
}
于 2020-03-21T03:13:57.400 回答