通过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. js兼容多浏览器的关闭当前页面

    关闭当前页面,相信不少人在开发中都遇到过这个需求,但面对这么多的浏览器,要做到js的兼容还需要做特殊的处理.关于这方面网上有很多的资料,但大多都是复制粘贴的,没有达到兼容的效果,或者是效果不好. 下面 ...

  2. Mybatis返回插入的主键

    在使用MyBatis做持久层时,insert语句默认是不返回记录的主键值,而是返回插入的记录条数:如果业务层需要得到记录的主键时,可以通过配置的方式来完成这个功能 情景一:针对自增主键的表,在插入时不 ...

  3. iPad中控制器view的width和height

    一.iPad中控制器view的width和height 1> 规律 * width 是宽高中最小的那个值 * height 是宽高中最大的那个值 2> 举例(比如窗口根控制器的view,有 ...

  4. 最大熵模型 Maximum Entropy Model

    熵的概念在统计学习与机器学习中真是很重要,熵的介绍在这里:信息熵 Information Theory .今天的主题是最大熵模型(Maximum Entropy Model,以下简称MaxEnt),M ...

  5. (六)6.10 Neurons Networks implements of softmax regression

    softmax可以看做只有输入和输出的Neurons Networks,如下图: 其参数数量为k*(n+1) ,但在本实现中没有加入截距项,所以参数为k*n的矩阵. 对损失函数J(θ)的形式有: 算法 ...

  6. 【英语】Bingo口语笔记(24) - L的发音技巧

    舌头往上跑

  7. 利用ICSharpCode.SharpZipLib.Zip进行文件压缩

    官网http://www.icsharpcode.net/ 支持文件和字符压缩. 创建全新的压缩包 第一步,创建压缩包 using ICSharpCode.SharpZipLib.Zip; ZipOu ...

  8. MIMO-OFDM通信系统学习笔记(一)

    [笔记一:单载波传输与多载波传输] MIMO-OFDM技术是3G-LTE,WiMAX通信系 统,以及WLan比如IEEE802.11a,IEEE802.11n等标准的关键技术,作为一枚通信狗,这些应该 ...

  9. ssl创建自签名的https通信

    ssl协议:ssl在tcp之上,http之下.兼容底层协议.所以推广起来很容易. create a self-signed server certificate -- for test purpose ...

  10. postgresql pg_hba.conf

    pg_hba.conf是客户端认证配置文件 METHOD指定如何处理客户端的认证.常用的有ident,md5,password,trust,reject. PostgreSQL默认只监听本地端口,用n ...