Routing Tables路由表

在Asp.Net Web API中,一个控制器就是一个处理HTTP请求的类,控制器的public方法就被叫做action方法或简单的Action。当Web API接收到一个请求的时候,它将这个请求路由到一个Action。

注意:Web API的路由与Asp.Net MVC的路由是非常相似的。主要区别就是Web API使用的是HTTP方法,而不是URI路径来选择Action

为了确定哪个Action被调用,这个框架使用了一个注册表。Visual Studio的Web API的项目模板会创建一个默认路由:

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

这个路由是在WebApiConfig文件中定义的,该文件位于App_Start目录

当Web API框架接收到一个HTTP请求时,它会试图根据路由表中的一个路由模板来匹配其URI。如果无路由匹配,客户端会接收到一个404(未找到)错误。例如,以下URI与这个默认路由的匹配

  • /api/product
  • /api/product/1
  • /api/product?category=category

然而,以下URI就不匹配,因为它缺少“api”字段

  • /product/1

注意:在路由中使用“api”的原因是为了避免与ASP.NET MVC的路由冲突。通过这种方式,可以用“/product”进入一个控制器,而“/api/product”进入一个Web API控制器。当然,如果你不喜欢这种约定,也可以修改这个默认路由表。

一旦一个匹配的路由被发现,Web API便会选择相应的Controller和Action。

1.为了找到Controller,Web API会把“控制器”加到{Controller}变量的值

2.为了找到Action,Web API会查找HTTP方法,然后寻找一个名称以HTTP方法名开头的方法。例如:对于Get请求,Web API会查找一个以“Get..”开头的Action,这种约定只应用于GET,POST,PUT,DELETE方法,通过在Controller上使用attribute,你可以启动其它的HTTP方法

3.路由模板中其它的占位变量;例如{id},将会被映射成Action的参数。

Routing Variations路由变化

HTTP方法

替代使用HTTP方法的命名约定,你可以明确的为一个Action指定HTTP方法,通过以HttpGet,HttpPost,HttpPut或者HttpDelete属性来对Action方法进行修身

在下列示例中,FindProduct方法被映射到GET请求

 [HttpGet]
public Product FindProduct(int id)
{
return repository.Get(id);
}

使用上面代码时需要先注释上面写的GetProduct(int id);

因为如果不注释 Web API会匹配到请求匹配的多个操作错误

Web API允许一个Action对应多个HTTP方法;

 [AcceptVerbs("GET","POST","HEAD")]
public Product FindProduct(int id)
{
return repository.Get(id);
}
[AcceptVerbs("MKCOL")]
public void MakeCollection()
{ }

第一个方法:指示该Action接收HTTP的GET,POST和HEAD方法。

第二个方法:WebDAV方法,(基于Web的分布式著作与版本控制的HTTP方法,是一个扩展的HTTP方法,MKCOL时隶属于WebDAV的一个方法,它在URI指定的位置创建集合)

通过Action名称路由

在默认的路由模板中,这个Web API使用HTTP方法去选择Action。然而,你也可以在URI中创建包含Action名的路由

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

在这个路由模板中,{action}参数命名了控制器的Action方法。采用这种风格,需要使用注解属性来指明所允许的HTTP方法。例如,假设你的控制器已有以下方法:

 [HttpGet]
public string Details(int id);

在这中情况下,一个GET请求“api/Product/Details/1”将会映射到这个Detail方法。这种风格的路由类似于Asp.Net MVC,而且可能与RPC式的API接近。

你也可以通过使用ActionName注解属性来覆盖动作名。在以下例子中,有两个Action映射到"api/product/thumbnail/id"。一个支持GET,一个支持POST

 [HttpGet]
[ActionName("Thumbnail")]
public HttpResponseMessage GetThumbnailImage(int id);
[HttpPost]
[ActionName("Thumbnail")]
public void AddThumbnailImage(int id);

NonActions

为了防止一个方法被当作Action所请求,可以使用NonAction注解属性。它对框架发送信号:这个方法不是以一个Action,即使它可能与路由规则匹配

 [NonAction]
public void IsNoAction();

Route Templates

路由模板看起来类似一个URI路径,但它可以具有占位符,并用{}来指示:

"api/{controller}/public/{category}/{id}"

当创建一个路由的时候,你可以为某些或所有占位符提供默认值

defaults: new { category = "all" }

你可以提供约束,它限制URI片段如何与占位符匹配

constraints: new { id = @"\d+" }   // 只有在“id”是一个或多个数字时才匹配

上面语句是通过正则表达式来限制片段的取值,上面的注释说明id片段只匹配一个或多个数字,因此URI中id片段必须是一个数字才能与这个路由进行匹配。

这个框架试图把URI路径中的片段与这个模板进行匹配。模板中文字必须严格匹配。一个占位符可以匹配任何值,除非你指定了约束。这个框架不会URI另外的部分,例如主机名或者一个查询字符串。这个框架会选择路由表中第一个匹配的路由。

这个有两个特殊的占位符:“{Controller}”和“{Action}”。

{Controller}提供控制器名

{Action} 提供动作名。在Web API中,通常的约定是忽略{Action}的。

Defaults(默认值)

如果你提供默认值,那么这个路由匹配缺少这些片段的URI。例如

 routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{category}",
defaults: new { category = "all" }
);

这个URI“http://localhost/api/products”与这个路由是匹配的。“{category}”片段将赋成了默认值“all”。

Route Dictionary(路由字段)

如果这个框架发现了一个匹配的URI,它会创建包含每个占位符值的字典。这个键值是不带{}的占位符名称。这个值取自于URI路径或是默认值。这个字段被存在IHttpRouteData对象中。在匹配路由阶段,这个特殊的{Controller}和{Action}占位符的处理和其它占位符是一样的,它们用另外的值被简单的存储在字典中。

在默认值中可以使用特殊的RouteParameter.Optional值。如果一个占位符被赋予了这个值,那么这个值将不会被添加到字典中,例如

 routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{category}/{id}",
defaults: new { category = "all", id = RouteParameter.Optional }
);

对于URI路径“api/product”,路由字典将含有:controller:"product",category:"all"

然而,对于”api/product/toys/123“,路由字典将含有:controller:"product",category:"toys"

这个默认值也可以包含未出现的路由模板中的值。若这条路由匹配,则该值会被存储在路由字典中。例如

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

如果URI路径是”api/root/7“,字典中将含有两个值:controller:"product",id:"8"。

Selecting a Controller

控制器选择是由IHttpControllerSelector.SelectController方法来处理的。这个方法以HttpRequestMessage实例为参数。并返回HttpControllerDescriptor

其默认实现是由DefaultHttpControllerSelector类提供的。这个类使用了一种很直接的算法:

1.查找路由字典的”controller“键。

2.取得这个键的值,并附加字符串”Controller“,以得到控制器的类型名。

3.用这个类型名查找Web API控制器

例如,如果路由字典的键-值对为”controller“=”product“,那么控制器类型便为”ProductController“。如果没有匹配,或多个匹配,Web API框架会给客户端返回一个错误。

对于步骤3,DefaultHttpControllerSelector使用IHttpControllerTypeResolver接口以获得Web API控制类型的列表。IHttpControllerTypeResolver的默认实现会返回所有符合以下条件的public类:

  1. 实现IHttpController的类
  2. 是非抽象类
  3. 名称以”Contoller“结尾的类

Action Selection

选择了控制器后,Web API框架会通过调用IHttpActionSelector.SelectAction方法来选择Action。这个方法以HttpControllerContext为参数,并返回HttpActionDescriptor。

这个默认实现是由ApiControllerActionSelector类提供的。为了选择一个Action,会查找以下方面:

  1. HTTP请求的方法
  2. 这个路由模板的action占位符
  3. 控制器中Action的参数

在查找选择算法之前,我们需要理解控制器Action的一些事情

控制器的哪些方法被看成为Action?当选择一个Action时,这个框架只考察控制器的public实例方法。而且,它会排除特殊名称的方法(构造器,事件,操作符,重载符等),以及集成自ApiController的类方法

HTTP Methods

Web API框架只会选择与请求的HTTP方法匹配的Action,确定如下

  1. 你可以用注解属性AcceptVerbs,HttpDelete,HttpGet,HttpPost,HttpOptions,HttpPatch,HttpPost或者HttpPut来指定HTTP方法
  2. 如果控制器方法名称以Get,Post,Put,Delete,Head,Options或Patch开头,那么根据这个约定,该Action将支持相应的HTTP方法。
  3. 如果以上都不是,那么这个方法将只支持Post请求。

Parameter Bindings

参数绑定是指Web API如何创建参数值。以下是参数绑定的默认规则:

1.简单类型取自URI

2.复杂类型取自请求正文

简单类型包括所有".NET框架简单类型",另外还有,DateTime,Decimal,Guid,String和TimeSpan。对于每一个Action,最多只有一个参数可以读取请求正文。

在这种背景下,Action选择算法如下

  1. 创建该控制器中与HTTP请求方法匹配的所有Action的列表
  2. 如果路由字典有Action条目,移除与该条目值不匹配的Action
  3. 试图将Action参数与该URI匹配,如下

a:针对每个Action,获得简单类型的参数列表,这是绑定得到URI参数的地方。该列表不包括可选参数

b:从这个列表中,试着在路由字典或是在URI查询字符串中,找到每个参数的匹配。匹配是与大小写无关的,且与参数顺序无关

c:选择这样的一个Action,在列表中的每个参数在URI中有一个匹配

d:如果满足这些条件的Action不止一个,选用参数匹配最多的一个。

4.忽略用[NonAction]注解属性标注的Action。

第3步可能会rang人困扰。其基本思想是,可以从URI,或请求体,或一个自定义绑定来获取参数值。对于来自URI的参数,我们希望确保URI在其路径(通过路由字典)或查询字符串中实际包含一个用于此参数的值。

例如,考虑以下Action

public void Get(int id)

其id绑定到URI。因此,这个Action只能匹配在路由字典或查询字符串包含了id值的URI

可选参数是一个例外,因为它们是可选的。对于可选参数,如果绑定不能通过URI获取它的值,是没关系的。

复杂类型是另一个原因的例外。一个复杂类型只能通过自定义绑定来绑定到URI。但是在这种情况下,Web API框架不能提前知道是否这个参数被绑定到一个特殊的URI。为了查明情况,这个框架需要调用这个绑定。选择算法的目的是在调用绑定之前根据静态描述来选择一个Action。因此,负责类型是属于匹配算法之外的。

Action选择之后,会调用所有参数绑定。

Summary:

  1. Action必须匹配请求的HTTP方法。
  2. Action名必须匹配路由字典中的Action条目,如果有的话。
  3. 对于Action的各个参数,如果参数来自URI,那么该参数名必须在路由字典或URI查询字符串中能够被找到(可选参数和复杂参数类型除外)
  4. 试图匹配最多数目的参数。最佳匹配可能是一个无参数的方法。

Extended Points

Web API为路由过程的某些部分提供了扩展点。

要为以上任一接口提供自己的实现,可使用HttpConfiguration对象的Services集合:

var config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IHttpControllerSelector), new MyControllerSelector(config));

Asp.Net Web API(三)的更多相关文章

  1. [转]ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    本文转自:http://www.cnblogs.com/parry/p/ASPNET_MVC_Web_API_digest_authentication.html 在前一篇文章中,主要讨论了使用HTT ...

  2. ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    在前一篇文章中,主要讨论了使用HTTP基本认证的方法,因为HTTP基本认证的方式决定了它在安全性方面存在很大的问题,所以接下来看看另一种验证的方式:digest authentication,即摘要认 ...

  3. 使用ASP.NET web API创建REST服务(三)

    本文档来源于:http://www.cnblogs.com/madyina/p/3390773.html Creating a REST service using ASP.NET Web API A ...

  4. ASP.NET Web API 2系列(三):查看WebAPI接口的详细说明及测试接口

    引言 前边两篇博客介绍了Web API的基本框架以及路由配置,这篇博客主要解决在前后端分离项目中,为前端人员提供详细接口说明的问题,主要是通过修改WebApi HelpPage相关代码和添加WebAp ...

  5. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  6. ASP.NET Web API Model-ModelBinder

    ASP.NET Web API Model-ModelBinder 前言 本篇中会为大家介绍在ASP.NET Web API中ModelBinder的绑定原理以及涉及到的一些对象模型,还有简单的Mod ...

  7. ASP.NET Web API Model-ValueProvider

    ASP.NET Web API Model-ValueProvider 前言 前面一篇讲解了Model元数据,Model元数据是在Model绑定中很重要的一部分,只是Model绑定中涉及的知识点比较多 ...

  8. ASP.NET Web API Model-ModelMetadata

    ASP.NET Web API Model-ModelMetadata 前言 前面的几个篇幅主要围绕控制器的执行过程,奈何执行过程中包含的知识点太庞大了,只能一部分一部分的去讲解,在上两篇中我们看到在 ...

  9. ASP.NET Web API 过滤器创建、执行过程(二)

    ASP.NET Web API 过滤器创建.执行过程(二) 前言 前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器 ...

随机推荐

  1. 三种读取HashMap的方式

    package com.biubiu.entity; import java.util.Collection; import java.util.HashMap; import java.util.I ...

  2. Python使用虚拟环境

    这里想象一下需求,写一个项目使用的一系列1.0版本的插件,现在要新写一个项目,需要用这些插件的2.0版本,该怎么办?都更新成2.0版本?这样之前的项目都没法维护了 这时我们需要一个虚拟环境,Pytho ...

  3. 网页如何展示PPT文档

    最近再做一个新项目,其中有一个难点,就是如何在网页上展示PPT,我网上找到了几种方法,但是真正符合我目前这个项目的就只有这一种方法了,       使用PowerPoint to Flash将ppt文 ...

  4. java 之 工厂模式(大话设计模式)

    在以前的文章里面讲述过简单工厂模式,见链接:http://www.cnblogs.com/zhuxiansheng/p/7873161.html 简单工厂模式解耦了客户端和实现的依赖,不过如果有再次扩 ...

  5. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  6. Java之多态

    一.多态 1.含义 一种类型,呈现多种状态.主要关注类多态.方法多态. 2.多态的前提:继承 使用父类引用指向子类对象: Animal a1 = new Cat(): Object a1 = new ...

  7. AFNetworking提示3840 Unescaped control character around character XXX

    处理办法:找到AFNetworking包中AFURLResponseSerialization.m文件在第250行修改代码如下: if (data.length > 0 && ! ...

  8. SQL Count(*)函数,GROUP_By,Having的联合使用

    COUNT(*)  函数返回在给定的选择中被选的行数. 语法:SELECT  COUNT(*) FROM  table 使用:现在有一个表,名叫app_category,从Navicat中可以看到表中 ...

  9. 微信小程序爬坑日记

    新公司上手小程序.30天,从入门到现在,还没放弃... 虽然小程序发布出来快一年了,爬坑的兄弟们大多把坑都踩平了.而我一直停留在"Hello World"的学习阶段.一来没项目,只 ...

  10. C#爬虫系列(二)——食品安全国家标准数据检索平台

    上篇对“国家标准全文公开系统”的国标进行抓取,本篇对食品领域的标准公开系统“食品安全国家标准数据检索平台”进行抓取. 平台地址:http://bz.cfsa.net.cn/db 一.标准列表 第一步还 ...