通过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. URAL1900 Brainwashing Device(dp)

    1900 二维dp挺好推 dp[i][j] = max(dp[i][j],dp[g][j-1]+o[i][i+1]-o[g][i+1])(i>g>=j-1) dp[i][j]表示第i个站台 ...

  2. AutoCompleteTextView与TextWatcher的结合

      /******************************************************************************************** * au ...

  3. WinCE5.0中文模拟器SDK(VS2005)的配置

    WinCE5.0中文模拟器SDK的安装过程不细说了,一路默认即可,下面主要介绍如何配置,使其能在VS2005中正常使用. 安装完成后,打开VS2005,点击菜单“工具”——“选项”——“设备工具”—— ...

  4. Qt之QProgressIndicator(等待提示框)

    简述 很早以前在网上看到一个纯代码实现的旋转动画感觉效果很不错,分享给大家.不得不说,条条大道通罗马,我们需要更多地创造... 详见:QProgressIndicator 简述 效果 源码 使用 更多 ...

  5. Qt之QHeaderView自定义排序(获取正确的QModelIndex)

    简述 前几节中分享过关于自定义排序的功能,貌似我们之前的内容已经可以很好地解决排序问题了,但是,会由此引发一些很难发现的问题...比如:获取QModelIndex索引错误. 下面,我们先来实现一个整行 ...

  6. DESCryptoServiceProvider加密、解密

    .net名称空间System.Security.Cryptography下DESCryptoServiceProvider类为我们提供了加密和解密方法,我们只需少许代码便可实现加密和解密. 稍感不托的 ...

  7. Java中传值与传引用

    不管Java参数类型是什么,一律传递参数的副本. <Thinking In Java>:“When you're passing primitives into a method,you ...

  8. 嵌入式 hi3518平台获取网关

    </pre><pre code_snippet_id="495447" snippet_file_name="blog_20141024_1_70650 ...

  9. [Everyday Mathematics]20150226

    设 $z\in\bbC$ 适合 $|z+1|>2$. 试证: $$\bex |z^3+1|>1. \eex$$

  10. IIS6到7,web.config的配置

    如果在IIS6中你的web.config中是以下配置: 这是在IIS6中我们习惯的经典模式的配置 < system.web>  " " " " &q ...