Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
421 views
in Technique[技术] by (71.8m points)

c# - Multiple Controller Types with same Route prefix ASP.NET Web Api

Is it possible to separate GETs and POSTs into separate API Controller types and accessing them using the same Route Prefix?

Here are my controllers:

[RoutePrefix("api/Books")]
public class BooksWriteController : EventStoreApiController
{
    [Route("")]
    public void Post([FromBody] CommandWrapper commandWrapper){...}
}

[RoutePrefix("api/Books")]
public class BooksReadController : MongoDbApiController
{
    [Route("")]
    public Book[] Get() {...}

    [Route("{id:int}")]
    public Book Get(int id) {...}
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Web API (1.x-2.x) does not support multiple attribute routes with the same path on different controllers. The result is a 404, because all the route matches more than one controller and at that point Web API will consider the result ambiguous.

Note that MVC Core does support this scenario note: MVC Core serves as both MVC & Web API.

If you choose to use Web API 2.11 (or newer) you can create a route constraint for the http method per controller and use it instead of the built in Route Attribute. The sample below shows that you can use RoutePrefix or directly Routes (like kmacdonald's answer).

using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Routing;

public class BooksWriteController : ApiController
{
    [PostRoute("api/Books")]
    public void Post() { }
}

[RoutePrefix("api/books")]
public class BooksReadController : ApiController
{
    [GetRoute]
    public void Get() { }

    [GetRoute("{id:int}")]
    public void Get(int id) { }
}

These two classes simplify the use of the constraint route attribute

class GetRouteAttribute : MethodConstraintedRouteAttribute
{
    public GetRouteAttribute(string template) : base(template ?? "", HttpMethod.Get) { }
}

class PostRouteAttribute : MethodConstraintedRouteAttribute
{
    public PostRouteAttribute(string template) : base(template ?? "", HttpMethod.Post) { }
}

This class allows adding constraints to the route generated

class MethodConstraintedRouteAttribute : RouteFactoryAttribute
{
    public MethodConstraintedRouteAttribute(string template, HttpMethod method)
        : base(template)
    {
        Method = method;
    }

    public HttpMethod Method
    {
        get;
        private set;
    }

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

This is just a standard route constraint, nit: you may want to cache the constraints object to reduce allocations.

class MethodConstraint : IHttpRouteConstraint
{
    public HttpMethod Method { get; private set; }

    public MethodConstraint(HttpMethod method)
    {
        Method = method;
    }

    public bool Match(HttpRequestMessage request,
                      IHttpRoute route,
                      string parameterName,
                      IDictionary<string, object> values,
                      HttpRouteDirection routeDirection)
    {
        return request.Method == Method;
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...