zoukankan      html  css  js  c++  java
  • [翻译]创建ASP.NET WebApi RESTful 服务(10)

    通过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/

  • 相关阅读:
    docker学习-docker核心技术
    Ecmall 中国地区设置
    lazyload.js实现图片异步载入
    inpyt 按钮变透明 边框
    vertical-align0 垂直对齐- 图片 兼容个浏览器
    div文字超出
    jquery满屏滚动代码
    $().index() 两种用法
    setTimeout不断重复执行
    background属性
  • 原文地址:https://www.cnblogs.com/tukzer/p/3664921.html
Copyright © 2011-2022 走看看