ASP.NET Web API 2.0新特性:Attribute Routing1
ASP.NET Web API 2.0新特性:Attribute Routing[上篇]
对于一个针对ASP.NET Web API的调用请求来说,请求的URL和对应的HTTP方法的组合最终决定了目标HttpController的类型和定义其中的目标Action方法。两者之间的映射是通过URL路由来完成的,ASP.NET Web API路由系统提供了一种便捷的方式使我们可以在统一的地方注册适用于所有HttpController的路由。
如果我们能够直接针对目标Action方法进行路由注册,那么我们就能够对路由规则进行细粒度的控制。从设计角度来讲,Web API采用REST架构风格,其URL更多是对目标资源的反映,定义在同一个HttpController中的众多Action方法体现在对一组相关资源的操作。目标资源往往具有一定地层次结构,RESTful的URL应该尽可能地反映这种层次目标资源的这种层次结构,能否针对具体的Action方法进行路由映射往往决定了我们能否设计出真正具有REST风格的URL。为此,ASP.NET Web API 2.0对现有路由系统进行了扩展,提供了一种基于特性的路由是我们可以直接将路由直接注册到具体的Action方法上。[本文已经同步到《How ASP.NET Web API Works?》]
目录
HttpRouteInfoProvider特性
开启针对特性的路由
基本路由注册
让URL模板能够尽可能反映资源的层次结构
为路由变量设置约束
通配符路由变量
缺省路由变量
设置URL前缀
指定路由名称
生成HttpRoute在路由表中的顺序
HttpRouteInfoProvider特性
我们将应用到目标Action方法上直接进行路由注册的特性称为HttpRouteInfoProvider特性,因为它实现了System.Web.Http.Routing.IHttpRouteInfoProvider接口。我们在上面已经提到过了,这种基于特性的路由注册方式仅仅是对ASP.NET Web API现有路由系统的扩展,应用到目标方法上用于路由注册的特性最终会被转换成相应的HttpRoute对象。顾名思义,HttpRouteInfoProvider特性旨在提供最终创建这个HttpRoute对象的相关信息。
HttpRouteInfoProvider提供的路由信息集中体现在IHttpRouteInfoProvider接口的几个属性。如下面的代码片断所示,IHttpRouteInfoProvider定义了 三个只读属性,其核心自然是通过属性RouteTemplate表示的URL模板。它的属性RouteName表示最终创建的HttpRoute对象的名称,另一个属性RouteOrder则决定了该HttpRoute对象在路由表中的位置,这个位置最终决定了其被选择的优先级。
1: public interface IHttpRouteInfoProvider
2: {
3: string RouteName { get; }
4: int RouteOrder { get; }
5: string RouteTemplate { get; }
6: }
读者朋友们可能对IHttpRouteInfoProvider接口不太熟悉,但是对实现它的两个特性应该已经很熟悉了,它们分别是AcceptVerbsAttribute和HttpVerbAttribute。在前面介绍“Action的选择”中我们将这两个特性视为用于选择目标Action方法所支持的HTTP方法的ActionHttpMethodProvider特性来介绍的,其实它们除了实现IActionHttpMethodProvider接口之外,还实现了接口IHttpRouteInfoProvider。
AcceptVerbsAttribute
如下所示的AcceptVerbsAttribute的完整定义,当我们将它应用到目标Action方法上的时候,除了以构造函数参数的形式指定所支持的HTTP方法列表(以字符串的形式)之外,还可以指定RouteName、RouteOrder和RouteTemplate属性值以注册一个针对目标方法的路由。
1: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
2: public sealed class AcceptVerbsAttribute : Attribute, IActionHttpMethodProvider, IHttpRouteInfoProvider
3: {
4: public AcceptVerbsAttribute(params string[] methods);
5:
6: public Collection<HttpMethod> HttpMethods { get; }
7: public string RouteName { get; set; }
8: public int RouteOrder { get; set; }
9: public string RouteTemplate { get; set; }
10: }
对于如上所示的AcceptVerbsAttribute的定义,有一个细节我们只得注意:应用在该类型的AttributeUsageAttribute特性将AllowMultiple属性设置为True,意味着多个AcceptVerbsAttribute特性可以同时应用到同一个Action方法上。对于路由映射来说就意味着我们可以为同一个Action方法注册多个路由,这使我们可以采用不同的URL来访问同一个Action方法。
HttpVerbAttribute
AcceptVerbsAttribute和HttpVerbAttribute的不同之处在于前者可以通过字符串的形式指定多个支持的HTTP方法,而后者仅仅针对某个具体的HTTP方法。如下所示的是HttpVerbAttribute的完整定义,我们从中可以看出表示URL模板的只读属性RouteTemplate需要在构造函数中指定,而另外两个属性RouteName和RouteOrder则是可读可写的。
1: public abstract class HttpVerbAttribute : Attribute, IActionHttpMethodProvider, IHttpRouteInfoProvider
2: {
3: protected HttpVerbAttribute(HttpMethod httpMethod);
4: protected HttpVerbAttribute(HttpMethod httpMethod, string routeTemplate);
5:
6: public Collection<HttpMethod> HttpMethods { get; }
7: public string RouteName { get; set; }
8: public int RouteOrder { get; set; }
9: public string RouteTemplate { get; }
10: }
针对7种常用的HTTP方法,ASP.NET Web API定义了7个继承自HttpVerbAttribute的类型,它们分别是具有如下定义的HttpGetAttribute、HttpPostAttribute、HttpPutAttribute、HttpDeleteAttribute、HttpPatchAttribute、HttpHeadAttribute和HttpOptionsAttribute。我们可以看到这7个具体的HttpVerbAttribute均定义了两个构造函数重载,如果需要使用它们来注册路由,需要在构造函数中指定相应URL模板。
1: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
2: public sealed class HttpGetAttribute : HttpVerbAttribute
3: {
4: public HttpGetAttribute();
5: public HttpGetAttribute(string routeTemplate);
6: }
7:
8: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
9: public sealed class HttpPostAttribute : HttpVerbAttribute
10: {
11: public HttpPostAttribute();
12: public HttpPostAttribute(string routeTemplate);
13: }
14:
15: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
16: public sealed class HttpPutAttribute : HttpVerbAttribute
17: {
18: public HttpPutAttribute();
19: public HttpPutAttribute(string routeTemplate);
20: }
21:
22: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
23: public sealed class HttpDeleteAttribute : HttpVerbAttribute
24: {
25: public HttpDeleteAttribute();
26: public HttpDeleteAttribute(string routeTemplate);
27: }
28:
29: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
30: public sealed class HttpPatchAttribute : HttpVerbAttribute
31: {
32: public HttpPatchAttribute();
33: public HttpPatchAttribute(string routeTemplate);
34: }
35:
36: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
37: public sealed class HttpHeadAttribute : HttpVerbAttribute
38: {
39: public HttpHeadAttribute();
40: public HttpHeadAttribute(string routeTemplate);
41: }
42:
43: [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
44: public sealed class HttpOptionsAttribute : HttpVerbAttribute
45: {
46: public HttpOptionsAttribute();
47: public HttpOptionsAttribute(string routeTemplate);
48: }
在完成了用于进行路由注册的两个HttpRouteInfoProvider特性(AcceptVerbsAttribute和HttpVerbAttribute)之后,我们以实例演示的形式介绍如何利用它们进行针对目标Action方法的路由映射。简单起见,我们只选择使用针对单一HTTP方法的HttpVerbAttribute特性来注册我们需要的路由。
开启针对特性的路由
相对于针对特定的路由(Attribute-based Routing),我们将传统的路由称为基于约定的路由(Convention-based Routing)。在默认的情况下针对特定的路由机制是关闭的,我们需要调用HttpConfiguration具有如下定义的扩展方法MapHttpAttributeRoutes打开这个开关。
1: public static class HttpConfigurationExtensions
2: {
3: //其他成员
4: public static void MapHttpAttributeRoutes(this HttpConfiguration configuration);
5: public static void MapHttpAttributeRoutes(this HttpConfiguration configuration, HttpRouteBuilder routeBuilder);
6: }
如上面的代码片断所示,这个扩展方法具有两个重载,其中一个方法接受一个类型为HttpRouteBuilder的参数。顾名思义,HttpRouteBuilder的作用在于根据应用的HttpRouteInfoProvider特性创建对应的HttpRoute。
基本路由注册
假设有一家专门负责销售影碟的网店,我们为它开发了一套Web API来提供影片的信息,同时供后台应用进行影片资源的维护。简单起见,我们只定义了如下一个表示影片的类型Movie,它仅仅封装了一部电影包含片名、类型、主演、导演、发行日期、对白语言和故事摘要在内的基本信息,属性ID作为它的唯一标识。
1: public class Movie
2: {
3: public Guid Id { get; set; }
4: public string Name { get; set; }
5: public IEnumerable<string> Genres { get; set; }
6: public IEnumerable<string> Starring { get; set; }
7: public string Director { get; set; }
8: public DateTime ReleaseDate { get; set; }
9: public string Language {get; set;}
10: public string Story { get; set; }
11: }
我们将用于操作影片资源的基本操作定义在如下一个名为MoviesController的HttpController中,5个基本的Action方法上均应用了针对某个HTTP方法的HttpVerbAttribute特性并指定了相应的URL模板。用于根据ID获取对应影片信息的Action方法GetMovieById采用的URL模板为“api/movies/{id}”,另一个Action方法GetMovieByStarring返回由指定的某个演员主演的影片列表,它采用的URL模板为“api/movies/starring/{starring}”。这两个Action方法均仅仅提供针对HTTP-GET请求的唯一支持,URL模板中定义的路由变量(“{id}”和“{starring}”)均映射到目标方法的同名参数上。
1: public class MoviesController: ApiController
2: {
3: [HttpGet("api/movies/{id}")]
4: public Movie GetMovieById(Guid id);
5:
6: [HttpGet("api/movies/starring/{starring}")]
7: public IEnumerable<Movie> GetMovieByStarring(string starring);
8:
9: [HttpPost("api/movies")]
10: public void Add(Movie movie);
11:
12: [HttpPut("api/movies")]
13: public void Update(Movie movie);
14:
15: [HttpDelete("api/movies/{id}")]
16: public void Delete(string id);
17: }

其他三个Action方法Add、Update和Delete分别进行添加、修改和删除操作,我们分别在它们上面应用了HttpPostAttribute、HttpPutAttribute和HttpDeleteAttribute来控制它们支持的HTTP方法和指定URL模板。
因为用于注册路由的HttpRouteInfoProvider特性直接应用到目标Action方法上,当ASP.NET Web API根据它们生成相应HttpRoute的时候可以解析出目标HttpController和Action的名称,所以我们使用HttpRouteInfoProvider特性进行路由注册的时候无需指定对应的路由变量(“{controller}”和“{action}”)。
针对定义在MoviesController中支持HTTP-GET请求的两个Action方法GetMovieById和GetMovieByStarring,我们可以直接通过浏览器访问匹配的URL来获取相应的影片信息。如右图所示,我们通过地址“/api/movies/b4e0bc49-568d-402b-acf0-37b65008723f”获取ID为“b4e0bc49-568d-402b-acf0-37b65008723f”的 影片。通过地址“/api/movies/starring/pacino”得到的是由Pacino主要的影片列表。
让URL模板能够尽可能反映资源的层次结构
我们说Web API操作的目标资源具有的层次化结构往往不是指的资源本身具有的物理结构,而只要体现在资源针对不同的调用请求而具有的逻辑结构。就我们的例子来说,目标资源就是一组影片列表而已,其数据本身并不具有复杂的物理结构,其层次化的逻辑结构体现在针对不同条件组合的查询。我们需要针对资源的逻辑结构设计对应的URL,比如:
- 获取由Pacino主演的剧情片:/api/movies/starring/pacino/drama
- 获取由Cameron导演的爱情片:/api/movies/director/cameron/romance
针对如上这两种具体的需求,我们可以按照在MoviesController中添加如下两个Action方法。方法GetMovieByStarringAndGenre采用的URL模板分别为“api/movies/starring/{starring}/{genre}”而方法GetMovieByDirectorAndGenre 采用的URL为“api/movies/director/{director}/{genre}”。
1: public class MoviesController : ApiController
2: {
3: //其他成员
4: [HttpGet("api/movies/starring/{starring}/{genre}")]
5: public IEnumerable<Movie> GetMovieByStarringAndGenre(string starring, string genre);
6:
7: [HttpGet("api/movies/director/{director}/{genre}")]
8: public IEnumerable<Movie> GetMovieByDirectorAndGenre(string director, string genre);
9: }

针对注册在这两个Action方法上的路由,我们可以采用匹配的URL获取相应的影片列表。如右图6-2所示,我们直接利用浏览器发送请求来调用上面定义的两个Action方法。我们采用地址“/api/movies/starring/pacino/crime”获取由Pacino主演的犯罪类型的影片列表,而另一个地址“/api/movies/director/brest/drama”则用于获取由Brest导演的剧情片列表。
通过上面的介绍我们知道两种类型的HttpRouteInfoProvider特性(AcceptVerbsAttribute和HttpVerbAttribute)均具有这样的特性:多个同一类型的特性可以同时应用到相同的Action方法上。如果我们按照如下的方式将针对不同条件组合(主演、导演和类型)的查询均定义在同一个FindMovies方法上,然后通过应用多个HttpGetAttribute特性注册多个采用不同URL模板的路由,那么是否也能解决上面这个问题呢?
1: public class MoviesController : ApiController
2: {
3: //其他成员
4: [HttpGet("api/movies/starring/{starring}")]
5: [HttpGet("api/movies/starring/{starring}/{genre}")]
6: [HttpGet("api/movies/director/{director}/{genre}")]
7: public IEnumerable<Movie> FindMovies(string starring, string director, string genre);
8: }

实际上这个Action方法FindMovies是没有办法按照应用在它上面的HttpGetAttribute特性指定的URL模板进行调用的。如左图所示,我们在浏览器中采用模式匹配的三个URL来调用Web API,但无一例外都得到一个状态为“404, Not Found”的响应,并提示根据请求的URL无法找在目标HttpController中找到一个匹配的Action。
既然我们我们已经明确将相应的路由通过HttpGetAttribute特性注册到Action方法FindMovies上了,但是为何ASP.NET Web API却说根据请求URL(该URL与注册路由的URL模式相匹配)在MoviesController中找不到匹配的Action呢?其实《ASP.NET Web API是如何根据请求选择Action的?》已经回答了这个问题。
在《ASP.NET Web API是如何根据请求选择Action的?》一文中,我们介绍了ASP.NET Web API针对请求选择目标Action会经历4轮筛选,其中第3轮筛选出的候选Action必须满足这样的条件:Action方法中定义的由URL提供的非缺省参数必须存在于当前请求的URL中。对于我们这个例子来说,三个请求采用的URL都不能完整地提供定义在FindMovies方法上的所有三个参数(starring、director和genre)。
那么既然“非缺省参数”才需要由请求的URL来提供,那么如果我们将参数定义成缺省参数是否可以解决这个问题呢?为此我们按照如下的方式将FindMovies方法的三个参数均定义成默认值为空字符串的可缺省参数。
1: public class MoviesController : ApiController
2: {
3: //其他成员
4: [HttpGet("api/movies/starring/{starring}")]
5: [HttpGet("api/movies/starring/{starring}/{genre}")]
6: [HttpGet("api/movies/director/{director}/{genre}")]
7: public IEnumerable<Movie> FindMovies(string starring = "", string director = "", string genre = "");
8: }

我们再次利用浏览器采用相同的URL来调用定义在MoviesController中的这个Action方法FindMovies,会得到如右图所示的输出结果,可见将Action方法的参数定义成可缺省的参数可以帮助ASP.NET Web API找到对应我们期望的目标参数,我们在项目中也可以利用这样的变成技巧。
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
ASP.NET Web API 2.0新特性:Attribute Routing1的更多相关文章
- 尝新体验ASP.NET Core 6预览版本中发布的最小Web API(minimal APIS)新特性
本文首发于<尝新体验ASP.NET Core 6预览版本中发布的最小Web API(minimal APIS)新特性> 概述 .NET开发者们大家好,我是Rector. 几天前(美国时间2 ...
- 一个ASP.NET Web API 2.0应用
在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.N ...
- How ASP.NET Web API 2.0 Works?[持续更新中…]
一.概述 RESTful Web API [Web标准篇]RESTful Web API [设计篇] 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 二.路由 ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- ASP.NET Web API 2 中的特性路由
ASP.NET MVC 5.1 开始已经支持基于特性的路由(http://attributerouting.net),ASP.NET WEB API 2 同时也支持了这一特性. 启用特性路 由只需要在 ...
- ASP.NET Web API 2.0 统一响应格式
传统实现 在搭建 Web API 服务的时候,针对客户端请求,我们一般都会自定义响应的 JSON 格式,比如: { "Data" : { "Id" : 100, ...
- web Servlet 3.0 新特性之web模块化编程,web-fragment.xml编写及打jar包
web Servlet 3.0 模块化 原本一个web应用的任何配置都需要在web.xml中进行,因此会使得web.xml变得很混乱,而且灵活性差,因此Servlet 3.0可以将每个Servlet. ...
- ASP.NET Web Api构建基于REST风格的服务实战系列教程
使用ASP.NET Web Api构建基于REST风格的服务实战系列教程[十]——使用CacheCow和ETag缓存资源 系列导航地址http://www.cnblogs.com/fzrain/p/3 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【开篇】【持续更新中。。。】
最近发现web api很火,园内也有各种大神已经在研究,本人在asp.net官网上看到一个系列教程,原文地址:http://bitoftech.net/2013/11/25/detailed-tuto ...
随机推荐
- iis配置网址(主机名)
一直以来,常常弄不成功关于网址的问题. 今天查了下资料 首先,找到你的文件:C:\Windows\System32\drivers\etc的hosts文件,直接用记事本打开 # Copyright ( ...
- Java数据结构与算法(1) - ch02有序表(OrderedArray)
有序表需要掌握的插入方法,删除方法和二分法查找方法. 插入方法: 从前往后找到比要插入的值大的数组项,将该数组项及之后的项均后移一位(从最后一项起依次后移),最后将要插入的值插入当前数组项. 删除方法 ...
- 【百度地图API】如何制作孪生姐妹地图?
原文:[百度地图API]如何制作孪生姐妹地图? 任务描述: 我想要两张一模一样的地图!我想要双子地图!我想要孪生姐妹地图! 好好好,统统满足大家! 在这里我不需要使用百度地图API提供的地图缩略图控件 ...
- Spark集群搭建简配+它到底有多快?【单挑纯C/CPP/HADOOP】
最近耳闻Spark风生水起,这两天利用休息时间研究了一下,果然还是给人不少惊喜.可惜,笔者不善JAVA,只有PYTHON和SCALA接口.花了不少时间从零开始认识PYTHON和SCALA,不少时间答了 ...
- unity3d插件Daikon Forge GUI 中文教程-1-Daikon Forge介绍
(游戏蛮牛首发)大家好我是孙广东官网提供了专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上,要观看是须要FQ的. 只是教程 ...
- 利用@media screen实现网页布局的自适应,@media screen and
开始研究响应式web设计,CSS3 Media Queries是入门.Media Queries,其作用就是允许添加表达式用以确定媒体的环境情况,以此来应用不同的样式表.换句话说,其允许我们在不改变内 ...
- centos安装zabbix集群监控(亲测无坑版)
一. 安装lemp环境 下载安装包:wget bbs.linuxtone.org/docs/autoinstall/lemp_auto_v1.0.6.tar.gz 包解压:tar zxvf lemp_ ...
- 【麦子学院】OpenCV教程函数总结
个自带样例. parter 1: No1. adaptiveskindetector.cpp 利用HSV空间的色调信息的皮肤检測,背景不能有太多与肤色相似的颜色.效果不是特别好. No2. bagof ...
- SQL点滴20—T-SQL中的排名函数
原文:SQL点滴20-T-SQL中的排名函数 提到排名函数我们首先可能想到的是order by,这个是排序,不是排名,排名需要在前面加个名次序号的,order by是没有这个功能的.还可能会想到ide ...
- ASP.NET MVC进阶
ASP.NET MVC进阶 一.ASP.NET MVC中的AJAX应用 首先,在ASP.NET MVC中使用自带的ajax功能,必须要导入2个js文件(顺序不能颠倒): ASP.NET MVC提供了2 ...