具有相同路由前缀 ASP.NET Web Api 的多个控制器类型

Posted

技术标签:

【中文标题】具有相同路由前缀 ASP.NET Web Api 的多个控制器类型【英文标题】:Multiple Controller Types with same Route prefix ASP.NET Web Api 【发布时间】:2014-05-30 10:46:26 【问题描述】:

是否可以将 GET 和 POST 分成不同的 API 控制器类型并使用相同的 Route Prefix 访问它们?

这是我的控制器:

[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) ...

【问题讨论】:

虽然它仍然是同一个控制器,但你可以做的一件事是让你的控制器成为一个部分类,将你的 GET 放在一个文件中,将你的 POST 放在另一个文件中。 【参考方案1】:

Web API (1.x-2.x) 不支持在不同控制器上具有相同路径的多个属性路由。结果是 404,因为所有路由都匹配多个控制器,此时 Web API 将认为结果不明确。

请注意,MVC Core 确实支持这种情况说明:MVC Core 既可用作 MVC 又可用作 Web API。

如果您选择使用 Web API 2.11(或更新版本),您可以为每个控制器的 http 方法创建一个路由约束,并使用它来代替内置的路由属性。下面的示例显示您可以使用 RoutePrefix 或直接使用 Routes(如 kmacdonald 的答案)。

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)  

这两个类简化了约束路由属性的使用

class GetRouteAttribute : MethodConstraintedRouteAttribute

    public GetRouteAttribute(string template) : base(template ?? "", HttpMethod.Get)  


class PostRouteAttribute : MethodConstraintedRouteAttribute

    public PostRouteAttribute(string template) : base(template ?? "", HttpMethod.Post)  

该类允许向生成的路由添加约束

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;
        
    

这只是一个标准的路由约束,尼特:您可能希望缓存约束对象以减少分配。

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;
    

【讨论】:

@YishaiGalatzer:有什么具体原因需要在这里自定义MethodConstraint 而不是重复使用System.Web.Http.Routing.HttpMethodConstraint? 重点是限制路线本身。内置属性仅在运行太晚的动作选择中起作用。 如果有人正在查看此答案并且在命名路线时遇到问题,然后使用 Url.Link(...) 构建路线。这里有一个类似的答案:***.com/questions/40892637/… 我看到的主要区别是附加接口以及可能将 HttpMethodConstraint 与一组 HttpMethods 一起使用。无论哪种情况,另一个答案都使路由命名仍然可以供参考。【参考方案2】:

您并不总是需要在控制器上指定RoutePrefix。您可以直接将路线放在网络方法上:

public class BooksWriteController : EventStoreApiController

    [Route("api/Books")]
    public void Post([FromBody] CommandWrapper commandWrapper)...


public class BooksReadController : MongoDbApiController

    [Route("api/Books")]
    public TaskTypeInfo[] Get() ...


    [Route("api/Books/id:int")]
    public TaskTypeInfo Get(int id) ...

但是,我想您的 RoutePrefix 在两个控制器上都可以正常工作。我认为RoutePrefix 属性与实际定义路线的Route 属性结合使用。这意味着只要您没有任何冲突的路线(这是一个大问题),您应该没问题。

【讨论】:

谢谢,这不是问题。问题是根据 GET 或 POST 选择正确的控制器类型【参考方案3】:

利用部分类。 Partial Classes and Methods - C# MSDN

创建两个文件:BooksController.Write.csBooksController.Read.cs 只有 RoutePrefix 一个文件,因为它们是同一个类,它会给你一个错误,说你在同一个类前加了两次前缀。

这两个文件将编译为一个类(因为它是一个类,但拆分为不同的文件)。

// File BooksController.Write.cs
[RoutePrefix("api/Books")]
public partial class BooksController : EventStoreApiController

    [Route("")]
    public void Post([FromBody] CommandWrapper commandWrapper)...


// File BooksController.Read.cs
public partial class BooksController : MongoDbApiController

    [Route("")]
    public Book[] Get() ...


    [Route("id:int")]
    public Book Get(int id) ...

【讨论】:

出现错误——部分声明不能指定不同的基类

以上是关于具有相同路由前缀 ASP.NET Web Api 的多个控制器类型的主要内容,如果未能解决你的问题,请参考以下文章

.Net Core Web API,根据值创建路由逻辑到具有相同路由前缀的不同控制器

(Asp.Net MVC / Web-Api)中具有相同身份验证系统的多个项目

在 ASP.NET Web API 中使用多个 Get 方法进行路由

ASP.NET-入门

ASP.NET Core 6框架揭秘实例演示[30]:利用路由开发REST API

Web API系列教程2.1 — ASP.NET Web API中的路由机制