问题

怎样集中的定义路由

解决方案

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

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

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

 config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional}
); public class OrdersController : ApiController
{
public Order Get(int id)
{
// 忽略逻辑
}
}

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

况。

工作原理

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

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

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

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

 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
string routeTemplate, object defaults) public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
string routeTemplate, object defaults, object constraints) public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
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

 public class BadOrdersController : ApiController
{
[HttpGet]
public Order FindById(int id)
{
// 忽略逻辑
} public Order GetById(int id)
{
// 忽略逻辑
} public Order Get(int id)
{
// 忽略逻辑
}
}

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

  • 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 使用集中式路由配置嵌套路由

 public class TeamsController : ApiController
{
public Team GetTeam(int id)
{
// 忽略逻辑
}
public IEnumerable<Team> GetTeams()
{
// 忽略逻辑
}
public IEnumerable<Player> GetPlayers(int teamId)
{
// 忽略逻辑
}
} config.Routes.MapHttpRoute(
name: "players",
routeTemplate: "api/teams/{teamid}/players",
defaults: new {controller = "teams"}
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

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

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

[水煮 ASP.NET Web API2 方法论](3-1)集中式路由的更多相关文章

  1. [水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置

    阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式 ...

  2. [水煮 ASP.NET Web API2 方法论](3-6)万能路由

    问题 定义什么样的路由,可以不会受请求参数类型和数量的限制,而被全部捕获? 解决方案 在路由模板中,给参数添加一个"*"前缀,例如 {*param},只要请求的 URL 能够和路由 ...

  3. [水煮 ASP.NET Web API2 方法论](3-4)设置路由可选项

    问题 怎么样创建一个路由,不管客户端传不传这个参数,都可以被成功匹配. 解决方案 ASP.NET WEB API 的集中式路由和属性路由都支持路由声明可选参数. 在用集中式路由中可以通过 RouteP ...

  4. [水煮 ASP.NET Web API2 方法论](3-8)怎样给指定路由配置处理器

    阅读导航 问题 解决方案 工作原理 代码演示 问题 如果仅仅针对指定的路由进行某些特定的消息处理,而不是应用于所有路由,我们应该怎么做呢? 解决方案 ASP.NET WEB API 的很多功能都内建了 ...

  5. [水煮 ASP.NET Web API2 方法论](3-5)路由约束

    问题 怎么样限制路由中参数的值. 解决方案 ASP.NET WEB API 允许我们通过 IHttpRouteConstraint 接口设置路由约束.集中式路由和直接式路由都可以使用 IHttpRou ...

  6. [水煮 ASP.NET Web API2 方法论](3-3)路由默认值

    问题 如何为路由中参数设置默认值. 解决方案 不管使用属性路由还是集中式路由,ASP.NET WEB API 都可以很方便的为路由定义默认参数.在每次客户端请求的时候,如果客户端没有传这些参数,框架会 ...

  7. [水煮 ASP.NET Web API2 方法论](3-2)直接式路由/属性路由

    问题 怎么样可以使用更贴近资源(Controller,Action)的方式定义路由. 解决方案 可以使用属性路由直接在资源级别声明路由.只要简单的在 Action 上使用属性路由 RouteAttri ...

  8. [水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)

    问题 我们想快速启动一个 ASP.NET Web API 解决方案. 解决方案 APS.NET 模板一开始就支持 ASP.NET Web API.使用模板往我们的项目中添加 Controller,在我 ...

  9. [水煮 ASP.NET Web API2 方法论](3-7)默认 Action 请求方式以及 NonActionAttribute

    问题 在 Controller 中有一个 public 的方法,但是又不想将这个 publlic 方法暴露成为一个 API. 解决方案 ASP.NET Web API 中,正常是通过 HTTP 谓词来 ...

随机推荐

  1. IOS子视图超过父视图frame后,无法交互响应

    确定第一响应者 当用户触发某一事件(触摸事件或运动事件)后,UIKit会创建一个事件对象(UIEvent),该对象包含一些处理事件所需要的信息.然后事件对象被放到一个事件队列中.这些事件按照先进先出的 ...

  2. Java RMI 简单实现

    提供远端访问的时候,我们首先需要定义远端能够访问哪些东西,在Java中,定义这类接口需要实现Remote接口 public interface Business extends Remote{ pub ...

  3. supervisor自启动

    supervisor自启动 其实自启动,也就是在主机开启的时候,执行了sudo supervisord -c /etc/supervisord.conf: 创建/usr/lib/systemd/sys ...

  4. 51 nod 1105 第K大的数

    1105 第K大的数 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 数组A和数组B,里面都有n个整数.数组C共有n^2个整数,分别是A[0] * ...

  5. HDU 4135 容斥

    问a,b区间内与n互质个数,a,b<=1e15,n<=1e9 n才1e9考虑分解对因子的组合进行容斥,因为19个最小的不同素数乘积即已大于LL了,枚举状态复杂度不会很高.然后差分就好了. ...

  6. ZeroMQ API(六) 代理

    1.zmq_proxy(3) 1.1 名称 zmq_proxy - 开始内置ZMQ代理 1.2 概要 int zmq_proxy(const void * frontend,const void * ...

  7. .NET 定时器类及使用方法

    Timer类实现定时任务 //2秒后开启该线程,然后每隔4s调用一次 System.Threading.Timer timer = new System.Threading.Timer((n) =&g ...

  8. 【整理】HTML5游戏开发学习笔记(1)- 骰子游戏

    <HTML5游戏开发>,该书出版于2011年,似乎有些老,可对于我这样没有开发过游戏的人来说,却比较有吸引力,选择自己感兴趣的方向来学习html5,css3,相信会事半功倍.不过值得注意的 ...

  9. spring boot获取前端参数四种方法

    一:直接参数绑定 @RequestMapping("/hello") @ResponseBody public String hello(String para) { // par ...

  10. win7.wifi热点

    使用本地连接上网,将网卡设为wifi热点 cmd 管理员身份运行 netsh wlan set hostednetwork mode=allow ssid=4Gtest key=12345678 网络 ...