• [水煮 ASP.NET Web API2 方法论](3-1)集中式路由


    问题

    怎样集中的定义路由

     

    解决方案

    通过调用 HttpRouteCollectionExtension 类中的 MapHttpRoute 扩展方法在 HttpRouteCollection 中定义路由,可以通过 HttpConfiguration 对象调用。

    最基础的使用就是定义一个非常通用的路由模板,他会通过 {controller} 占位符匹配所有的 Controller。如代码片段 3-1 所示。

     

    代码片段 3-1. ASP.NET WEB API 默认定义的路由以及一个简单的 Controller

     1 config.Routes.MapHttpRoute(
     2     name: "DefaultApi",
     3     routeTemplate: "api/{controller}/{id}",
     4     defaults: new {id = RouteParameter.Optional}
     5     );
     6 
     7  
     8 
     9 public class OrdersController : ApiController
    10 {
    11     public Order Get(int id)
    12     {
    13         // 忽略逻辑
    14     }
    15 }

    在路由模板中,可以定义自己的占位符,如代码片段 3-1 所示的 {id}。他与 Action 中的参数名称相匹配匹配,ASP.NET WEB API 会从 request 中提取出相应的值,传入 Action 方法中。也就是,代码片段 3-1 中 OrdersController 的 Get请求方法的这种情

    况。

     

    工作原理

    从最初版本开始,ASP.NET WEB API 就一直使用集中式路由维护路由表,这和 MVC 如出一辙。

    ASP.NET WEB API 定义了很多 MapHttpRoute 的变种。所需参数最少的一个方法,只需要一个路由模板和一个路由名称。

    1 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
    2     string routeTemplate)

    除声明简单基础路由以外的方式,也可以通过默认值和约束,或者设置每个路由消息处理程序,将会在下面的章节介绍这个。所有的这些操作都是通过 Map HttpRoute 的重载方法实现的。

    1 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
    2     string routeTemplate, object defaults)
    3  
    4 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
    5     string routeTemplate, object defaults, object constraints)
    6  
    7 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
    8     string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)

    通过路由默认值,可以直接定位到路由相关的 Controller;可是,这样的特定路由需要定义在通用路由之前,也就是代码片段 3-1 的路由之前。原因就是,路由是顺序匹配的,路由在选定处理一个 HTTP 请求的时候,是在每次请求的时候,扫描路由集合中的所有路由,选用第一个被匹配的路由模板。简单的说,越是特殊的路由定义越靠前,越是通用的路由定义越靠后。

     

    在  Self-hosting 中,ASP.NET WEB API 是以 IDictionary<stirng, IHttpRoute> 的形式在维护路由,Idictionary<string, IHttpRoute> 是在 HttpRouteCollection 类中。在 Web host 中也提供了 System.Web.Routing.RouteCollection 的扩展方法,因此,可以直接在这个里面定义 ASP.NET WEB API 路由。

    RouteTable.Routes.MapHttpRoute("DefaultApi", "api/{controller}")

    如果使用 ASP.NET 运行时 host WEB API,无论使用什么扩展方法声明路由,都会被添加到同一个 RouteTable 中。内部是通过一个叫做 HostedHttpRouteCollection 的类来实现的,这个类是 HttpRouteCollection 的子类,而不是在字典(比如 self-host)中维护路由,所有转发路由都是查找 System.Web.RouteCollection。

     

    ASP.NET WEB API 与 ASP.NET MVC 是不一样的,API 使用的是基于 HTTP 谓词匹配来处理一个 Controller 请求。换句话说,框架会根据 HTTP 的东西来选择一个 Action 处理请求,选择的逻辑如下:

    • 通过方法名推断 HTTP 谓词,如果名字类似于 PostOrder、GetById 等等。
    • 通过 Action 属性推断 HTTP 谓词。
    • Action 参数定义与路由模板必须匹配。

     

    典型的 ASP.NET WEB API 路由是指向资源的。可以通过 HTTP  谓词调用,还需要除了 Action 之外的参数。如代码片段 3-1 所示的默认路由,可以匹配如下请求:

    • GET myapi.com/api/orders
    • POST myapi.com/api/inovice
    • PUT myapi.com/api/invice/2

     

    小提示 在 ASP.NET WEB API 是可以使用 RPC 的,具体细节会在 3-6 介绍。

     

    代码演示

    ASP.NET WEB API 是基于 HTTP 谓词进行分发逻辑的,不像 ASP.NET MVC,很容易出现 AmbiguousMatchEception。例如,可以设想一些使用代码片段 3-1 的路由的例子。

    如代码片段 3-2 所示的例子,这三个方法都是可以处理相同的 GET 请求,如,/api/orders/1.

    代码片段 3-2.

     

    代码片段 3-2. ASP.NET WEB API Controller

     1 public class BadOrdersController : ApiController
     2 {
     3     [HttpGet]
     4     public Order FindById(int id)
     5     {
     6         // 忽略逻辑
     7     }
     8  
     9     public Order GetById(int id)
    10     {
    11         // 忽略逻辑
    12     }
    13  
    14     public Order Get(int id)
    15     {
    16         // 忽略逻辑
    17     }
    18 }

    同时我们需要注意,定义复杂、多等级、嵌套路由的时候,集中式路由变得有点麻烦。考虑如下路由

    • GET myapi.com/api/teams
    • GET myapi.com/api/teams/1
    • GET myapi.com/api/teams/1/players

     

    使用集中式路由,可以在 Controller 中使用如下三个方法;然而,我们必须注意路由之间冲突的问题,因为有两个 GET 方法都是只使用了一个 int 的参数。如代码片段 3-3 所示。有一个特殊的路由指出了 URI 中包含 /players/ 段会被匹配到,而且这个路由定义在通用路由之前。

     

    代码片段 3-3 使用集中式路由配置嵌套路由

     1 public class TeamsController : ApiController
     2 {
     3     public Team GetTeam(int id)
     4     {
     5         // 忽略逻辑
     6     }
     7     public IEnumerable<Team> GetTeams()
     8     {
     9         // 忽略逻辑
    10     }
    11     public IEnumerable<Player> GetPlayers(int teamId)
    12     {
    13         // 忽略逻辑
    14     }
    15 }
    16 
    17  
    18 
    19 config.Routes.MapHttpRoute(
    20     name: "players",
    21     routeTemplate: "api/teams/{teamid}/players",
    22     defaults: new {controller = "teams"}
    23     );
    24 config.Routes.MapHttpRoute(
    25 name: "DefaultApi",
    26 routeTemplate: "api/{controller}/{id}",
    27 defaults: new { id = RouteParameter.Optional }
    28 );

    集中式路由的主要问题是,特殊路由的定义,仅仅是处理特殊的 Controller 特殊的 Action。通过定义特殊路由并添加到一般路由之前,在人为干预下短路了路由匹配。

     

    这种处理并不是最理想,当应用程序变大之后,会有更复杂的、多层级的路由,可能就要在集中式路由中痛苦的挣扎(路由维护和调试)。更好的选择就是使用直接式路由,下一篇 3-2 定义直接路由,以及后面介绍路由的时候都会涉及直接式路由。

  • 相关阅读:
    java中volatile关键字的含义
    2019年个人总结
    跟随Javac代码来解答字节码的疑惑
    Python装饰器实现带参数和不带参数
    try with resource当中你没有注意到点。。
    IDEA中,已经被加到版本库的文件如何在提交的时候忽略它们
    连接mysql客户端报错: java.sql.SQLException: Unable to load authentication plugin 'caching_sha2_password'
    从字节码层次看i++和++i
    对lambda表达式的字节码实现个人理解
    解决mvn clean install的报错The packaging for this project did not assign a file to the build artifact
  • 原文地址:https://www.cnblogs.com/shuizhucode/p/6096338.html
Copyright © 2020-2023  润新知