通过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. Oracle数据库之三

    子查询 -- 就是在一个查询中包含多个select语句(一般就2个) select id,first_name,dept_id from s_emp; 想查询和Ben一个部门的员工的id,first_ ...

  2. Large Object Heap内存碎片在.NET 4.5中的改进

    .NET 4.5已然到来,预览了解了下Large Object Heap在.NET 4.5中的效能改进.借此和大家来探讨下.本文不讨论Loder Heap,SOH(samll object heap) ...

  3. 线程——委托InvokeRequired和Invoke

    C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它.此时它将会在内部调用ne ...

  4. 使用Aspose.Cells组件生成Excel文件

    生成带表头的Excel文件,格式如下显示. 当然更复杂的一些也可以通过 合并单元格的方法 public void Merge(int firstRow, int firstColumn, int to ...

  5. UVa 11039 (排序+贪心) Building designing

    白书上的例题比较难,认真理解样例代码有助于提高自己 后面的练习题相对简单,独立思考解决问题,增强信心 题意:n个绝对值各不相同的非0整数,选出尽量多的数排成序列,使得该序列正负交错且绝对值递增. 解法 ...

  6. UVa 1587 Box

    题意:给出6个矩形的长和宽,问是否能够构成一个长方体 先假设一个例子 2 3 3 4 2 3 3 4 4 2 4 2 排序后 2 3 2 3 3 4 3 4 4 2 4 2 如果要构成一个长方体的话, ...

  7. Asp.Net验证码3

    using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System. ...

  8. linux安全体系

    一.   硬件.软件选型 确认需求,分析可能面临的安全问题 记录各硬件寿命.保证散热.确保性能冗余 使用商业正版.开源的软件,不从互联网安装系统,从源头保证系统安全 购买专业售后支持 服务器配置双电源 ...

  9. BZOJ 1827 奶牛大集会

    树型DP. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm& ...

  10. Python [Leetcode 345]Reverse Vowels of a String

    题目描述: Write a function that takes a string as input and reverse only the vowels of a string. Example ...