惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
G
Google Developers Blog
J
Java Code Geeks
The GitHub Blog
The GitHub Blog
F
Full Disclosure
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Vercel News
Vercel News
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Announcements
Recent Announcements
Help Net Security
Help Net Security
The Hacker News
The Hacker News
IT之家
IT之家
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
V
Visual Studio Blog
博客园 - 聂微东
Hacker News: Ask HN
Hacker News: Ask HN
H
Hacker News: Front Page
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
Security Archives - TechRepublic
Security Archives - TechRepublic
Simon Willison's Weblog
Simon Willison's Weblog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Troy Hunt's Blog
Last Week in AI
Last Week in AI
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
博客园 - 【当耐特】
有赞技术团队
有赞技术团队
AWS News Blog
AWS News Blog
Blog — PlanetScale
Blog — PlanetScale
博客园_首页
Google DeepMind News
Google DeepMind News
Cloudbric
Cloudbric
N
News | PayPal Newsroom
A
About on SuperTechFans
S
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
雷峰网
雷峰网
T
The Exploit Database - CXSecurity.com
罗磊的独立博客
K
Kaspersky official blog
The Cloudflare Blog
I
Intezer

博客园 - laughter

使用Using的注意事项 [转]VMPlayer的Briged网络配置 [转]移动互联网应用技术架构简介-Restful服务 理解WCF中的Contracts [翻译]在ASP.NET Web API中通过OData支持查询和分页 [转]NBehave行为驱动测试关于story和scenarios [转]使用TeamCity对项目进行可持续集成管理(一) [转]SpecFlow使用入门 [转]软件测试- 3 - Mock 和Stub的区别 [翻译]创建ASP.NET WebApi RESTful 服务(11) [翻译]创建ASP.NET WebApi RESTful 服务(9) [转]模拟HttpContext 实现ASP.NET MVC 的单元测试 [翻译]创建ASP.NET WebApi RESTful 服务(8) [翻译]创建ASP.NET WebApi RESTful 服务(7) [转]软件开发过程(CMMI/RUP/XP/MSF)是与非? [转]比较 Rational Unified Process (RUP) 和 Microsoft Solutions Framework (MSF) [翻译]Behavior-Driven Development (BDD)行为驱动开发(二) [翻译]Behavior-Driven Development (BDD)行为驱动开发(一) 【Visual Studio2010】创建XAML分析插件
[翻译]创建ASP.NET WebApi RESTful 服务(10)
laughter · 2014-04-14 · via 博客园 - laughter

通过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:                  );

这种方式虽然解决了问题,其实违背了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:      }

代码的主要内容包括:

  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)));

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

将这些逻辑加入到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:      }

接下来可以用Fiddler进行测试,并在Request Header中添加一条对应消息就可。

API Version by Custom 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:      }

这段代码手从MediaType为application/json的Header中检索version信息,下图是Fiddler的测试情况。

Web API Version by Accept header

这是一种更为规范的方法,因为无须添加新的自定义Header,也不用使用不同的URI。

如果你订阅了Plural sight并且有继续研究的兴趣的话,那么你可以查看Shawn Wildermuth课程,他介绍了其他的版本管理方式。

来源:http://bitoftech.net/2013/12/16/asp-net-web-api-versioning-accept-header-query-string/