通过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; }

代码的主要内容包括:

  1. 通过GetControllerMapping()方法获取所有从ApiController对象派生的Dictionary集合;

  2. 根据request.GetRouteData()方法检索路由数据,然后查找对应request的Controller;
  3. 通过Controller的名称,获取HttpControllerDescriptor对象,它包含了对应Controller的信息;
  4. 如果以上都成功,那就证明我们可以使用类似的技术实现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)的更多相关文章

  1. [翻译]创建ASP.NET WebApi RESTful 服务(8)

    本章讨论创建安全的WebApi服务,到目前为止,我们实现的API都是基于未加密的HTTP协议,大家都知道在Web中传递身份信息必须通过HTTPS,接下来我们来实现这一过程. 使用HTTPS 其实可以通 ...

  2. [翻译]创建ASP.NET WebApi RESTful 服务(9)

    一旦成功的发布API后,使用者将依赖于你所提供的服务.但是变更总是无法避免的,因此谨慎的制定ASP.NET Web API的版本策略就变得非常重要.一般来说,新的功能需要无缝的接入,有时新老版本需要并 ...

  3. [翻译]创建ASP.NET WebApi RESTful 服务(7)

    实现资源分页 本章我们将介绍几种不同的结果集分页方式,实现手工分页,然后将Response通过两个不同的方式进行格式化(通过Response的Envelop元数据或header). 大家都知道一次查询 ...

  4. [翻译]创建ASP.NET WebApi RESTful 服务(11)

    本章介绍通过使用Ali Kheyrollahi开发的CacheCow来实现服务器端的缓存.所有代码现在都可以在GitHub上下载. 我们将要实现的缓存方式叫做Conditional Requests, ...

  5. IIS 部署ASP.Net, WebAPI, Restful API, PUT/DELETE 报405错解决办法, webapi method not allowed 405

    WebDAV 是超文本传输协议 (HTTP) 的一组扩展,为 Internet 上计算机之间的编辑和文件管理提供了标准.利用这个协议用户可以通过Web进行远程的基本文件操作,如拷贝.移动.删除等.在I ...

  6. Asp.net WebAPi Restful 的实现和跨域

    现在实际开发中用webapi来实现Restful接口开发很多,我们项目组前一段时间也在用这东西,发现大家用的还是不那么顺畅,所以这里写一个Demo给大家讲解一下,我的出发点不是如何实现,而是为什么? ...

  7. 在windows10上创建ASP.NET mvc5+Memcached服务

    感谢两位两位大佬: https://blog.csdn.net/l1028386804/article/details/61417166 https://www.cnblogs.com/running ...

  8. 让Asp.Net WebAPI支持OData查询,排序,过滤。

    让Asp.Net WebAPI支持OData后,就能支持在url中直接输入排序,过滤条件了. 一.创建Asp.Net WebAPI项目: 二.使用NuGet安装Asp.Net WebAPI 2.2和O ...

  9. ASP.NET WebAPI 双向token实现对接小程序登录逻辑

    最近在学习用asp.net webapi搭建小程序的后台服务,因为基于小程序端和后台二者的通信,不像OAuth(开放授权),存在第三方应用.所以这个token是双向的,一个是对用户的,一个是对接口的. ...

随机推荐

  1. URAL1495. One-two, One-two 2(dp)

    1495 牵扯一点数位 保存数的时候我是按2进制保存的 把1当作0算 把2当作1算 滚动数组 dp[i][j][(g*10+j)%n] = min(dp[i][j][(g*10+j)%n],dp[i- ...

  2. EF5&MVC4 学习1、创建新的Contoso University Application,并创建Model Class 生成对应的database

    参考:http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/creating-an-entity-framewo ...

  3. 将js对象转为json对象属性加上引号

    工具地址 http://js2json.mengxiangchaoren.com

  4. HDU 2516 (Fabonacci Nim) 取石子游戏

    这道题的结论就是,石子的个数为斐波那契数列某一项的时候,先手必败:否则,先手必胜. 结论很简单,但是证明却不是特别容易.找了好几篇博客,发现不一样的也就两篇,但是这两篇给的证明感觉证得不清不楚的,没看 ...

  5. UVa 247 Calling Circles【传递闭包】

    题意:给出n个人的m次电话,问最后构成多少个环,找出所有的环 自己想的是:用map来储存人名,每个人名映射成一个数字编号,再用并查集,求出有多少块连通块,输出 可是map不熟,写不出来,而且用并查集输 ...

  6. 编译安装apache+php(加常见问题解决)

    [编译apache]./configure --prefix=/usr/local/lamp/httpd -with-apr=/usr/local/apr -with-apr-util=/usr/lo ...

  7. python执行mysqldump命令

    本文简单讲述如何利用python执行一些sql语句,例如执行mysqldump命令,进行数据库备份,备份成sql文件 #!/usr/bin/python#导入os模块import os#导入时间模块i ...

  8. 4.0之后的hibernate获取sessionFactory

    static{ Configuration config=new Configuration().configure(); ServiceRegistry resgistry = new Servic ...

  9. HDU 5339 Untitled (暴力枚举)

    题意:给定一个序列,要求从这个序列中挑出k个数字,使得n%a1%a2%a3....=0(顺序随你意).求k的最小值. 思路:排个序,从大的数开始模起,这是因为小的模完还能模大的么? 每个元素可以选,也 ...

  10. LeetCode Reverse Nodes in k-Group 每k个节点为一组,反置链表

    题意:给一个单链表,每k个节点就将这k个节点反置,若节点数不是k的倍数,则后面不够k个的这一小段链表不必反置. 思路:递归法.每次递归就将k个节点反置,将k个之后的链表头递归下去解决.利用原来的函数接 ...