[翻译]创建ASP.NET WebApi RESTful 服务(10)
通过URI实现版本管理
另一种实现版本管理的方式就是通过URI来进行处理,类似于http://localhost:{your_port}/api/v1/students/。这种方式的好处是使用者可以清楚的知道当前自己使用的版本。实现也很简单:
1: config.Routes.MapHttpRoute(
2: name: "Students",
3: routeTemplate: "api/v1/students/{userName}",
4: defaults: new { controller = "students", userName = RouteParameter.Optional }
5: );
6:
7: config.Routes.MapHttpRoute(
8: name: "Students2",
9: routeTemplate: "api/v2/students/{userName}",
10: defaults: new { controller = "studentsV2", userName = RouteParameter.Optional }
11: );
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
这种方式虽然解决了问题,其实违背了REST的特性,因为路由规则总是在不停的变动。
Controller Selector技术
在引入第三种技术之前,先回顾下Web API的路由原理。在DefaultHttpControllerSelectro类中有个方法SelectController,它接受一个HttpRequestMessage类型的对象,以键值对方式存储了各种路由数据(包括各种Controller的名称,这些都定义在WebApiConfig文件中)。通过这些信息,可以借助反射技术检索所有的从ApiController类派生的对象然后进行匹配。如果出现重复或者没有找到,则抛出异常。否则,返回对应的Controller对象。
要重写这种默认实现,就需要从Http.Dispatcher.DefaultHttpControllerSelector派生一个类,假设叫做LearningControllerSelector,然后可以重写SelectController,代码如下:
1: public class LearningControllerSelector : DefaultHttpControllerSelector
2: {
3: private HttpConfiguration _config;
4: public LearningControllerSelector(HttpConfiguration config)
5: : base(config)
6: {
7: _config = config;
8: }
9:
10: public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
11: {
12: var controllers = GetControllerMapping(); //Will ignore any controls in same name even if they are in different namepsace
13:
14: var routeData = request.GetRouteData();
15:
16: var controllerName = routeData.Values["controller"].ToString();
17:
18: HttpControllerDescriptor controllerDescriptor;
19:
20: if (controllers.TryGetValue(controllerName, out controllerDescriptor))
21: {
22:
23: var version = "2";
24:
25: var versionedControllerName = string.Concat(controllerName, "V", version);
26:
27: HttpControllerDescriptor versionedControllerDescriptor;
28: if (controllers.TryGetValue(versionedControllerName, out versionedControllerDescriptor))
29: {
30: return versionedControllerDescriptor;
31: }
32:
33: return controllerDescriptor;
34: }
35:
36: return null;
37:
38: }
39: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
代码的主要内容包括:
- 通过GetControllerMapping()方法获取所有从ApiController对象派生的Dictionary集合;
- 根据request.GetRouteData()方法检索路由数据,然后查找对应request的Controller;
- 通过Controller的名称,获取HttpControllerDescriptor对象,它包含了对应Controller的信息;
- 如果以上都成功,那就证明我们可以使用类似的技术实现ControllerNameV2的路由。
现在先做一件事,就是将自定义的Controller Selector替换默认,实现方法是在WebApiConfig文件的Register方法中进行处理。
1: config.Services.Replace(typeof(IHttpControllerSelector), new LearningControllerSelector((config)));
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
通过QueryString实现版本管理
接下来的工作就比较直接,可以直接在URI的后面附加“?V=2”这样的请求,例如http://localhost:{your_port}/api/students/?v=2。如果没有附加类似的信息,那么就默认为最旧的版本。
添加一个函数,用来实现这一过程。
1: private string GetVersionFromQueryString(HttpRequestMessage request)
2: {
3: var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
4:
5: var version = query["v"];
6:
7: if (version != null)
8: {
9: return version;
10: }
11:
12: return "1";
13:
14: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
将这些逻辑加入到SelectorController的逻辑中即可实现按需调用。当然,这种实现方法还是会违背REST的原则,因为URI仍然是不固定的。
通过自定义Header实现版本管理
简单说,就是在Header信息中添加一条记录,使用者在请求的信息中添加一条版本信息,然后再ControllerSelector的地方进行处理。简单的实现代码如下:
1: private string GetVersionFromHeader(HttpRequestMessage request)
2: {
3: const string HEADER_NAME = "X-Learning-Version";
4:
5: if (request.Headers.Contains(HEADER_NAME))
6: {
7: var versionHeader = request.Headers.GetValues(HEADER_NAME).FirstOrDefault();
8: if (versionHeader != null)
9: {
10: return versionHeader;
11: }
12: }
13:
14: return "1";
15: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
接下来可以用Fiddler进行测试,并在Request Header中添加一条对应消息就可。
这种方法的不足之处就是必须在Headers中添加了自定义的Header信息,因此继续介绍另一种方法。
通过AcceptHeader进行版本管理
接下来我们就使用Accept Header实现版本管理,最终的GET请求Header应该是这样的“Accept:application/json; version=2”。因此我们可以修改为如下的逻辑:
1: private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request)
2: {
3: var acceptHeader = request.Headers.Accept;
4:
5: foreach (var mime in acceptHeader)
6: {
7: if (mime.MediaType == "application/json")
8: {
9: var version = mime.Parameters
10: .Where(v => v.Name.Equals("version", StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
11:
12: if (version != null)
13: {
14: return version.Value;
15: }
16: return "1";
17: }
18: }
19: return "1";
20: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
这段代码手从MediaType为application/json的Header中检索version信息,下图是Fiddler的测试情况。
这是一种更为规范的方法,因为无须添加新的自定义Header,也不用使用不同的URI。
如果你订阅了Plural sight并且有继续研究的兴趣的话,那么你可以查看Shawn Wildermuth的课程,他介绍了其他的版本管理方式。
来源:http://bitoftech.net/2013/12/16/asp-net-web-api-versioning-accept-header-query-string/
[翻译]创建ASP.NET WebApi RESTful 服务(10)的更多相关文章
- [翻译]创建ASP.NET WebApi RESTful 服务(8)
本章讨论创建安全的WebApi服务,到目前为止,我们实现的API都是基于未加密的HTTP协议,大家都知道在Web中传递身份信息必须通过HTTPS,接下来我们来实现这一过程. 使用HTTPS 其实可以通 ...
- [翻译]创建ASP.NET WebApi RESTful 服务(9)
一旦成功的发布API后,使用者将依赖于你所提供的服务.但是变更总是无法避免的,因此谨慎的制定ASP.NET Web API的版本策略就变得非常重要.一般来说,新的功能需要无缝的接入,有时新老版本需要并 ...
- [翻译]创建ASP.NET WebApi RESTful 服务(7)
实现资源分页 本章我们将介绍几种不同的结果集分页方式,实现手工分页,然后将Response通过两个不同的方式进行格式化(通过Response的Envelop元数据或header). 大家都知道一次查询 ...
- [翻译]创建ASP.NET WebApi RESTful 服务(11)
本章介绍通过使用Ali Kheyrollahi开发的CacheCow来实现服务器端的缓存.所有代码现在都可以在GitHub上下载. 我们将要实现的缓存方式叫做Conditional Requests, ...
- IIS 部署ASP.Net, WebAPI, Restful API, PUT/DELETE 报405错解决办法, webapi method not allowed 405
WebDAV 是超文本传输协议 (HTTP) 的一组扩展,为 Internet 上计算机之间的编辑和文件管理提供了标准.利用这个协议用户可以通过Web进行远程的基本文件操作,如拷贝.移动.删除等.在I ...
- Asp.net WebAPi Restful 的实现和跨域
现在实际开发中用webapi来实现Restful接口开发很多,我们项目组前一段时间也在用这东西,发现大家用的还是不那么顺畅,所以这里写一个Demo给大家讲解一下,我的出发点不是如何实现,而是为什么? ...
- 在windows10上创建ASP.NET mvc5+Memcached服务
感谢两位两位大佬: https://blog.csdn.net/l1028386804/article/details/61417166 https://www.cnblogs.com/running ...
- 让Asp.Net WebAPI支持OData查询,排序,过滤。
让Asp.Net WebAPI支持OData后,就能支持在url中直接输入排序,过滤条件了. 一.创建Asp.Net WebAPI项目: 二.使用NuGet安装Asp.Net WebAPI 2.2和O ...
- ASP.NET WebAPI 双向token实现对接小程序登录逻辑
最近在学习用asp.net webapi搭建小程序的后台服务,因为基于小程序端和后台二者的通信,不像OAuth(开放授权),存在第三方应用.所以这个token是双向的,一个是对用户的,一个是对接口的. ...
随机推荐
- Android在OnCreate中获取控件的宽度和高度
在Android中,有时需要对控件进行测量,得到的控件宽度和高度可以用来做一些计算.在需要自适应屏幕的情况下,这种计算就显得特别重要.另一方便,由于需求的原因,希望一进入界面后,就能得到控件的宽度和高 ...
- source idea of Unit
After the construction of Global environment setting code, there is a convenient way for us in the f ...
- 用Maven插件生成Mybatis代码
现在代码管理基本上是采用Maven管理,Maven的好处此处不多说,大家用百度搜索会有很多介绍,本文介绍一下用Maven工具如何生成Mybatis的代码及映射的文件. 一.配置Maven pom.xm ...
- 51nod1394 差和问题
我只会用线段树写...不喜欢树状数组..其实跑的也不算慢?然后各种*的时候忘了longlong一直WA...药丸! 而且我不怎么会用map离散化...那么就sort+unique #include&l ...
- 51nod1189 阶乘分数
(x-n!)(y-n!)=n!2 ans=t[n]+1.t表示的是n!2的小于n!的约数个数.n!2=p1a1*p2a2*p3a3...t[n]=(a1+1)*(a2+1)...-1 /2; 2对于n ...
- php开启curl扩展
配置方法: 1.拷贝PHP目录中的libeay32.dll 和 ssleay32.dll 两个文件到 system32 目录. 2.修改php.ini:配置好 extension_dir ,去掉 ex ...
- xxx_cast类型转换
xxx_cast是一个统称,它指的是static_cast(静态转换),const_cast(常量转换),reinterpert_cast(重解释转换),dynamic_cast(动态转换).本次我们 ...
- 07day1
怒跪了. 砍树 排序 [问题描述] 小 A 在一条水平的马路上种了 n 棵树,过了几年树都长得很高大了,每棵树都可以看作是一条长度为 a[i]的竖线段.由于有的树过于高大,挡住了其他的树,使得另一 ...
- 四:分布式事务一致性协议paxos通俗理解
转载地址:http://www.lxway.com/4618606.htm 维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的"La" ...
- Android 开源项目DiskLruCache 详解
有兴趣的同学可以读完这篇文章以后 可以看看这个硬盘缓存和volley 或者是其他 图片缓存框架中使用的硬盘缓存有什么异同点. 讲道理的话,其实硬盘缓存这个模块并不难写,难就难在 你要考虑到百分之0.1 ...