Asp.Net Web API 2第六课——Web API路由和动作选择

简介: Asp.Net Web API 导航       Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html       Asp.

Asp.Net Web API 导航

      Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html

      Asp.Net Web API第二课——CRUD操作http://www.cnblogs.com/aehyok/p/3434578.html

      Asp.Net Web API第三课——.NET客户端调用Web API http://www.cnblogs.com/aehyok/p/3439698.html

      Asp.Net Web API第四课——HttpClient消息处理器 http://www.cnblogs.com/aehyok/p/3442277.html

  Asp.Net Web API第五课——Web API路由 http://www.cnblogs.com/aehyok/p/3442051.html

前言

  本文描述ASP.NET Web API如何把一个HTTP请求路由到控制器的一个特定的Action上。关于路由的总体概述可以参见上一篇教程 http://www.cnblogs.com/aehyok/p/3442051.html。这篇文章主要来学习路由过程的细节。如果你创建了一个Web API项目,发现有一些请求没有按照你期望的方式被路由,希望这篇文章将对你有所帮助。

      本文主要分为三个阶段:

  1.匹配URI到一个Route Template。

  2.选择一个Controller。

  3.选择一个Action。

  你可以用自己的自定义行为来替换这一过程中的某些部分。在本文中,我将来描述默认的行为。在文章结尾,我会注明可以在什么地方自定义行为。

 

  

Route Templates

 路由模版看上去类似于一个URI路径,但它可以具有占位符,并用花括号来指示:

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

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

defaults: new { category = "all" }

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

constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.

上面语句是通过正则表达式来限制片段的取值,上面的注释说明 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}" and "{action}"占位符的处理和其他占位符是一样的。它们用另外的值被简单的存储在字典中。

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

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

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

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

 

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

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

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

Selecting a Controller

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

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

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

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

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

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

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

a:实现IHttpController的类。

b:是非抽象类。

c:名称以“Controller”结尾的类。

Action Selection

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

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

  1.HTTP请求的方法。

  2.这个路由模板中的“action”占位符。

  3.控制器中动作的参数。

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

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

 

HTTP Methods

这个框架只会选择与请求的HTTP方法匹配的动作,确定如下:

  1.你可以用注解属性AcceptVerbs、HttpDelete、HttpGet、HttpHead、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。对于每一个动作,最多只有一个参数可以读取请求正文。

它也可以重写这种默认的绑定规则。See WebAPI Parameter binding under the hood

在这种背景下,动作选择算法如下:

1.创建该控制器中与HTTP请求方法匹配的所有动作的列表。

2.如果路由字典有“action”条目,移除与该条目值不匹配的动作。

3.试图将动作参数与该URI匹配,如下:

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

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

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

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

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

 

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

例如,考虑以下动作:

public void Get(int id)

其id参数绑定到URI。因此,这个动作只能匹配在路由字典或查询字符串中包含了“id”值的URI。

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

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

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

Summary:

1.动作必须匹配请求的HTTP方法。

2.动作名必须匹配路由字典中的“action”条目,如果有。

3.对于动作的各个参数,如果参数取自URI,那么该参数名必须在路由字典或URI查询字符串中能够被找到。(可选参数和复杂类型除外)。

4.试图匹配最多数目的参数。最佳匹配可能是一个无参数的方法。

Extended Example

看如下路由:

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

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

再看如下Contoller下的内容:

public class ProductsController : ApiController 
{ 
    public IEnumerable<Product> GetAll() {} 
    public Product GetById(int id, double version = 1.0) {} 
    [HttpGet] 
    public void FindProductsByName(string name) {} 
    public void Post(Product value) {} 
    public void Put(int id, Product value) {} 
}

 

 

HTTP请求:

http://localhost:34701/api/products/1?version=1.5&details=1

路由匹配:

该URI与名为“DefaultApi”路由匹配。路由字典包含以下条目:controller:"products",id:"1"。该路由字典并未包含查询字符串参数“version”和“details”,但这些将在动作选择期间考虑。

控制器选择:

根据路由字典中的“controller”条目,控制器类型是ProductsController。

动作选择:

这个HTTP请求是一个GET请求。支持Get的控制器动作是GetALL、GetById、FindProductsByName。这个路由字典不包含”action“条目,因此不需要匹配动作名称。

下一步,会试图匹配这些动作的参数名,只考查GET动作。

注意,不会考虑GetById的version参数,因为它是一个可选参数。

GetAll方法非常匹配。GetById方法也匹配,因为路由字典包含了“id”。FindProductsByName方法不匹配。

GetById方法是赢家,因为它匹配了一个参数,而GetAll无参数。该方法将以以下参数值被调用:id=1,version=1.5

注意,虽然version未被用于选择算法,但该参数值会取自URI查询字符串。

Extension Points

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

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

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

总结

 这一篇好难懂,无奈之下多查了一下,就感觉肯定早有人翻译过了,还真是就在博客园就有,我之前咋就没发现呢http://www.cnblogs.com/r01cn/archive/2012/12/04/2801158.html,感觉大神翻译的真心赞,好好看就看懂了,为了提高自己阅读英语的水平,看一句英文,看一句翻译,然后自己再逐字敲一遍。这一篇下来真心累了。不过自己对Asp.Net MVC的路由机制也有了新的认识,不错。

参考原文链接http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection

 

目录
相关文章
|
2月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
170 3
|
1月前
|
开发框架 .NET 程序员
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
Autofac 是一个轻量级的依赖注入框架,专门为 .NET 应用程序量身定做,它就像是你代码中的 "魔法师",用它来管理对象的生命周期,让你的代码更加模块化、易于测试和维护
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
|
28天前
|
开发框架 .NET PHP
ASP.NET Web Pages - 添加 Razor 代码
ASP.NET Web Pages 使用 Razor 标记添加服务器端代码,支持 C# 和 Visual Basic。Razor 语法简洁易学,类似于 ASP 和 PHP。例如,在网页中加入 `@DateTime.Now` 可以实时显示当前时间。
|
2月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
110 9
|
3月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
3月前
|
缓存 中间件 网络架构
Python Web开发实战:高效利用路由与中间件提升应用性能
在Python Web开发中,路由和中间件是构建高效、可扩展应用的核心组件。路由通过装饰器如`@app.route()`将HTTP请求映射到处理函数;中间件则在请求处理流程中插入自定义逻辑,如日志记录和验证。合理设计路由和中间件能显著提升应用性能和可维护性。本文以Flask为例,详细介绍如何优化路由、避免冲突、使用蓝图管理大型应用,并通过中间件实现缓存、请求验证及异常处理等功能,帮助你构建快速且健壮的Web应用。
37 1
|
4月前
|
API
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
|
4月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
61 0
|
4月前
|
存储 开发框架 .NET
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
|
4月前
|
开发框架 .NET API
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
230 0