WebAPI 2.0发布和删除路由

本文关键字:删除 路由 0发 WebAPI | 更新日期: 2023-09-27 17:51:24

我在同一个控制器上有两个动作,具有相同的路由,但单独的HttpMethod要求(POST vs DELETE)。

[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

WebAPI 2.0发布和删除路由

怎么回事

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");
    }
}

[HttpDelete("DeleteEmployee/{id}")]public async Task DeleteEmployee(Guid id)