Web Api 2(三)之路由与Action的选择

路由(Route

  Web Api中的路由与Asp.net mvc中的路由基本上一样,一个路由看起来像是一个URI路径,但是路由中包含一些大括号包括的占位符(place holder),例如:
  
  api/{Controller}/{Action}/{Id}

  当你创建一个路由的时候,你可以为一个或多个占位符设置默认值,例如下面的例子将Controller设为Account,如果请求访问的URL没有提供Controller时,将会使用设置的默认的 Controller
  
  defaults:new {Controller=Account}

   你还可以为占位符设置一些限制,下面的例子限制Id只能为数字

  constraints:new{ id = @"\d+"}


  Web Api中的路由主要有三个主要的功能:

  - 将请求的URI与路由模板进行匹配
  - 选择Controller
  - 选择Action
  
  当收到请求是,Web Api会尝试着将请求URI与路由表(Route Table)中注册的路由模板进行比对,模板中的字面值(Literals)必须被准确的匹配,如上面例子中的api片段,请求的URL必须包含api才能匹配上面的路由,而占位符可以匹配任何值(除非你有其它特殊的限制),将请求的URL进行匹配时,只会比对路由模板中有的片段(Segment),而路由模板中没有的则不会比对,如主机名(host name)、查询参数(query parameters)等,之后选择路由表中匹配的第一个路由来进行下一步的处理
  
  Web Api提供的默认路由如下:

	 config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

  default中设置的id = RouteParameter.Optional表示路由模板中id是不是必须的,而是可选的,只有在请求的URL存在时才会被赋值


路由字典(Route Dictionary

如果请求的URL匹配到了与之匹配的路由时,Web api会创建一个Dictionary来存放URL中与路由模板中占位符对应的字段值,这个Dictionary的Key就是路由模板的占位符的名称(不包括大括号),这些值即可能来自URL,也可能来自注册路由时设置的默认值。而这个Dictionary就存放IHttpRouteData类型的对象中。


注意 : 如果路由中的占位符被设置为 RouteParameter.Optional,那么这个参数的值(即RouteParameter.Optional)不会被加入到上述的Dictionary中。但如果这个占位符被分配了具体的值的话,这个Dictionary中还是会存储这个值的,例如:api/products,那么此时这个Dictionary中存储的就是:

  • controller: "products"
  • category:"all"

  并不会存储: id:RouteParameter.Optional,在比如,api/products/toys/123,那么此时这个Dictionary中存储的就是:

  • controller:"products"
  • catagory: "toys"
  • id : "123"

  这个字典中甚至可以包含路由模板中不存在,但在默认值中存在的值,例如:

	routes.MapHttpRoute(
		name:"Root",
		routeTemplate:"api/root/{id}",
		default:new { controller="customers",id = RouteParameter.Optional}
	)

  如果URL中分离出的片段为api/root/8,那么此时这个Dictionary中存储的是:

  • controller:"customers
  • id : "8"


> #### Controller的选择

  Controller的选择是通过IHttpControllerSelector.SelectController 的方法来实现的,这个方法接收一个** HttpRequestMessage类型对象返回一个 HttpControllerDescriptor **,默认使用的是Web api内部提供的 DefaultHttpControllerSelector,这个类使用下面的逻辑来选择Controller:

  1. "Controller"为Key到上述的Dictionary(存储在IHttpRouteData类型中)查找对应的Value
  2. 在获取到的值(即请求的Controller名称)后面附加"Controller"字符串去获取对应的Controller的类型名称
  3. 拿第二步得到的Controller的名称去获取对应的Web Api Controller对象(ApiController类型)

  例如,如果路由字典(Route Dictionary)包含键值对(key-value pair)"Controller=products",那么控制器的类型便是"ProductsController",如果没有找到与之匹配的ApiController类型或者有多个匹配,则返回一个错误给请求的客户端。

  在第三步中,DefaultHttpControllerSelector 使用 IHttpControllerTypeResolver(典型的IOC应用)接口去获取所有的ApiController类型(均派生在ApiController),这个接口返回满足一下条件的所有公共(Public)类型:

  • 实现IHttpController接口
  • 非抽象类
  • 类名以"Controller"结束

> #### Action的选择

  在得到匹配的Controller类型后,Web Api会调用IHttpActionSelector.SelectAction方法去选择Action,这个方法接收一个HttpControllerContext类型参数,返回一个HttpActionSelector类型实例。选择Action的大致过程如下:

  1. 查看请求的Http Method(Get、POST等),获取请求的Action名称,方法和获取Controller名称的方法一致,以"Action"为Key到路由字典中获取对应值
  2. 查看路由表中注册的路由模板中的默认值
  3. 查看Controller中定义的Action方法的参数,找出参数最匹配的一个Action

  那么,满足什么条件的方法才能被视为一个Action呢?满足如下条件的方法便会被认为是Action

  • 定义在Controller中的所有公共的实例方法(不包括一些具有特殊名称的方法,如构造函数、事件、运算符重载等)

  Web Api仅仅选择那些与请求的Http Method匹配的Action:

  • 一个Action如果没有特殊说明,那么它的Http Method为POST
  • 你可以通过[HttpGet]、[HttpPost]、[HttpDelete]、[HttpHead]、[HttpOptions]、[HttpPatch]、[HttpPut] 特性来显式的声明一个Action的支持的Http Method,也可以通过HttpVerbs特性来声明。
  • 如果一个Action的名称以PostGet、Patch、Head、Put、Options、Delete等字符开头的,则认为该Action支持对应的Http Method。

Action 的参数绑定

  Web Api中,Action的参数的创建过程称为参数的绑定,遵照一下的规则:

  • 简单类型(Simple Type)的参数从URI中获取其参数值
  • 复杂类型(Complex Type)的参数从Http的请求报文中获取其参数值

  那么如何区分简单类型和复杂类型呢?在Web Api中,如果一个数据类型支持源自字符串的类型转换,那么该数据对象就是简单类型,否则,该数据对象就是复杂类型,按照这个判断的标准,Net中的所有的基元类型(Primative Type)和可空值类型(Nullable Type)、外加 DateTime, Decimal, Guid, String, and TimeSpan.都是简单类型,而一个自定义的类型默认情况下都是复杂类型.对于一个Action来说,之多有一个参数来自于Http请求报文

注: 在C#中,能够直接被编译器识别的类型称为基元类型.如int、byte等,其分别对应着FCL中的System.Int32System.Byte



  了解了上面的东西后,我们看一下Action 选择的机制(简单参数类型)

  1. 将 Controller中所有满足请求Http Method的Action放入一个列表中
  2. 如果路由字典中存在"Action"条目,则从创建好的Action列表中移除名称不匹配的Action.
  3. 尝试着通过URI去获取Action的参数
  • 对于每个Action,获取其简单参数类型的参数列表,这其中不包括可选参数(Optional Parameters
  • 试着根据参数名称从参数列表、路由字典(Route Dictionary)或查询字符串(Query String)中获取对应的参数值,这个匹配过程参数名称不区分大小写,也不依赖于参数的顺序。
  • 选择一个Action,其参数列表中的每个参数在URI都有与之对应的参数值
  • 如果有多个Action满足条件,则选择一个最为匹配的一个
  1. 忽略那些应用了[NonAction]特性的Action

> #### 其它

  Web Api的路由过程是支持扩展的,当内置的实现逻辑不足以满足应用的需求时,此时,便可以通过实现相应的接口来对逻辑进行自定义。其中相关的接口及其作用如下:

接口 描述
IHttpControllerSelector 选择控制器
IHttpControllerResolver 获取控制器类型列表,默认的DefaultHttpControllerSelector
获取的控制器类型列表中选择控制器
IAssemblyResolver 获取项目的程序集列表,IHttpControllerTypeResolver使用这个列表去查找控制器类型
IHttpControllerActivator 创建控制器实例
IHttpActionSelector 选择Action
IHttpActionInvoker 执行Action

在实现某个接口后,然后使用HttpConfiguration类的Service集合去注册后,Web Api便会使用自定义的逻辑去替换掉内置的默认实现。
	var config = GlobalConfiguration.Configuration;
	config.Services.Replace(typeof(IHttpControllerSelector),new MyControllerSelector(config));
posted @ 2016-08-26 15:16  空城守望城空  阅读(2175)  评论(2编辑  收藏  举报