【问题标题】:WebAPI 2.0 Post and Delete RoutesWebAPI 2.0 发布和删除路由
【发布时间】:2015-08-15 16:33:09
【问题描述】:

我在同一个控制器上有两个动作,具有相同的路由,但 HttpMethod 要求不同(POSTDELETE)。

[AllowAnonymous]
public class TestController : ApiController
{
    [Route("~/api/test")]
    [HttpDelete]
    public IHttpActionResult Endpoint1()
    {
        return this.Ok("endpoint1");
    }

    [Route("~/api/test")]
    [HttpPost]
    public IHttpActionResult Endpoint2()
    {
        return this.Ok("endpoint2");
    }
}

这一切都很好——从DELETE 切换到POST 时,两个端点都可以工作。

例如

DELETE /api/test = endpoint1
POST /api/test = endpoint2

如果我将动作分成单独的控制器,它就不再起作用了:

[AllowAnonymous]
public class TestController : ApiController
{
    [Route("~/api/test")]
    [HttpDelete]
    public IHttpActionResult Endpoint1()
    {
        return this.Ok("endpoint1");
    }
}

[AllowAnonymous]
public class TestController2 : ApiController
{
    [Route("~/api/test")]
    [HttpPost]
    public IHttpActionResult Endpoint2()
    {
        return this.Ok("endpoint2");
    }
}

例如

DELETE /api/test = endpoint1
POST /api/test = { "Message": "The requested resource does not support http method 'POST'." }

这是框架预期的吗?

编辑: 确切的 WebAPI 包版本是:5.2.3

【问题讨论】:

  • 1.最好指定您正在使用的 Web API 的版本。 2.路由中的“~/”不是必需的,“api/test”可以正常工作
  • 为什么不将所有与控制器相关的http动词都放在一个控制器中?
  • @YishaiGalatzer: 1. 更新到指定版本。 2.不管~与否,都会出现同样的问题。
  • @woogy -- 我在大多数情况下都这样做 -- 但这篇文章的目的是显示在不按控制器分组时发现的问题,而是显示他们正在执行的操作。
  • 是的 - ~/ 只是风格

标签: c# asp.net-web-api asp.net-web-api-routing


【解决方案1】:

发生了什么

Web API 2.0 不允许路由在两个不同的控制器上匹配。这在 MVC 6(它是 Web API 组合框架)中得到了解决。

我能做些什么

首先像@woogy,你说,这不是一个很常见的模式,所以大多数用户不应该去这里(或者当它进入 RTM 时转移到 MVC 6)。

根本原因是路由实际匹配,IActionHttpMethodProvider 定义的动词不限制路由匹配,它在多个控制器上匹配因此失败。

但是,您可以在路由上定义一个约束,并作为副作用获得更简洁的 API。

让我们开始吧

定义动词约束

这将限制路由只匹配预定义的动词,因此它不会匹配其他控制器。

public class VerbConstraint : IHttpRouteConstraint
{
    private HttpMethod _method;

    public VerbConstraint(HttpMethod method)
    {
        _method = method;
    }

    public bool Match(HttpRequestMessage request,
                      IHttpRoute route,
                      string parameterName,
                      IDictionary<string, object> values,
                      HttpRouteDirection routeDirection)
    {
        // Note - we only want to constraint on the outgoing path
        if (routeDirection == HttpRouteDirection.UriGeneration || 
            request.Method == _method)        
        {
            return true;
        }

        return false;
    }
}

为新属性定义一个抽象基类

public abstract class VerbRouteAttribute : RouteFactoryAttribute, IActionHttpMethodProvider
{
    private string _template;
    private HttpMethod _method;

    public VerbRouteAttribute(string template, string verb)
        : base(template)
    {
        _method = new HttpMethod(verb);
    }

    public Collection<HttpMethod> HttpMethods
    {
        get
        {
            var methods = new Collection<HttpMethod>();
            methods.Add(_method);

            return methods;
        }
    }

    public override IDictionary<string, object> Constraints
    {
        get
        {
            var constraints = new HttpRouteValueDictionary();
            constraints.Add("verb", new VerbConstraint(_method));
            return constraints;
        }
    }
}

这个类合并了 3 个东西 1. 带有路由模板的路由属性 2. 对路由应用动词路由约束 3.指定动作方法选择器,以便系统的其余部分(如帮助页面)识别它就像[HttpPost] / [HttpDelete]

现在让我们定义实现

public class PostRouteAttribute : VerbRouteAttribute
{
    public PostRouteAttribute(string template) : base(template, "POST")
    {
    }
}

public class DeleteRouteAttribute : VerbRouteAttribute
{
    public DeleteRouteAttribute(string template) : base(template, "DELETE")
    {
    }
}

正如您所见,这些都是非常琐碎的,只是在您的代码中使用这些属性更加顺畅。

最后让我们应用新属性(并移除方法属性)

[AllowAnonymous]
public class TestController : ApiController
{
    [DeleteRoute("api/test")]
    public IHttpActionResult Endpoint1()
    {
        return this.Ok("endpoint1");
    }
}

[AllowAnonymous]
public class TestController2 : ApiController
{
    [PostRoute("api/test")]
    public IHttpActionResult Endpoint2()
    {
        return this.Ok("endpoint2");
    }
}

【讨论】:

  • 感谢@Yishai 非常详细的解释。这确实有效 - 但会很高兴地等待 MVC 6 :)
【解决方案2】:

[HttpDelete("DeleteEmployee/{id}")] 公共异步任务 DeleteEmployee(Guid id)

【讨论】:

    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多