博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第十一节:WebApi的版本管理的几种方式
阅读量:6457 次
发布时间:2019-06-23

本文共 6154 字,大约阅读时间需要 20 分钟。

一. 背景和方案

1. 多版本管理的概念

  Android 、IOS等 App 存在着多版本客户端共存的问题:App 最新版已经升级到了5.0 了,但是有的用户手机上还运行着 4.8、3.9 甚至2.2 版本的 App,由于早期没有内置升级机制、用户不会升级、用户拒绝升级等原因,造成这些旧版本 App 也在运行。开发新版本 App 的时候,要给接口增加新的功能或者修改以前接口的规范,会造成旧版本App 无法使用,因此在一定情况下会“保留旧接口的运行、新功能用新接口”,这样就会存在多版本接口共存的问题。

  通常的做法是:旧版接口做一个代码分支,除了进行 bug 修改外,旧版本接口不再做改动,新接口代码继续演化升级。在客户端请求的时候带着要请求的接口版本号,在服务器端选择合适的版本代码进行处理。

2. 解决方案

(1). 不同的版本使用不同的域名:v1.api.ypf.com、v2.api.ypf.com、v3……  (最佳方案)

(2). 在Url,报文头等中带不同的版本信息,用Nginx等做反向代理服务,然后将 和 转到不同的服务器处理。

(3). 多个版本的 Controller共处在一个项目中,然 后使 用 [RoutePrefix] 特性来进行区分,这种方案Controller的名字不能一样,如下:

 

(4). 如果我想在Controller文件夹中新建多个版本文件夹,如:v1、v2、v3,每个文件夹中存放的控制器名称相同,比如都叫PersonController,不同文件夹下代表不同版本,这个时候会有一个很尴尬的问题,没法请求,识别不了,这个时候就需要重写系统默认的机制,IHttpControllerSelector 根据 “报文头”或者“请求路径”等选择不同的 Controller 执行。

该方案的实现,详见下面的实战测试

 

 

二. 实战测试

 1. 在Controller文件下新建v1和v2文件夹,分别存放不同版本的Person控制器,每个Person控制器新建一个GetName方法,如下图:

 

 2. 重写系统默认的控制器选择机制,可以直接实现IHttpControllerSelector接口,也可以继承DefaultHttpControllerSelector类,从而间接实现IHttpControllerSelector接口,在重写的SelectController方法中,实现了两种区分机制,分别是根据请求路径区分 和 根据报文头中的ApiVersion参数区分。

1     ///  2     /// 自己实现IHttpControllerSelector接口来替换系统默认的IHttpControllerSelector 3     ///  4     public class VersionConstrollerSelector : DefaultHttpControllerSelector 5     { 6  7         private HttpConfiguration _config; 8         ///  9         /// 构造函数10         /// 11         /// 12         public VersionConstrollerSelector(HttpConfiguration config) : base(config)13         {14             _config = config;15         }16 17 18         /// 19         /// 获取所有的Controller20         /// 21         /// 
22 public override IDictionary
GetControllerMapping()23 {24 Dictionary
dict = new Dictionary
();25 //循环所有的程序集26 foreach (var asm in _config.Services.GetAssembliesResolver().GetAssemblies())27 {28 //获取所有继承ApiController的非抽象类29 var controllerTypes = asm.GetTypes().Where(t => !t.IsAbstract && typeof(ApiController).IsAssignableFrom(t)).ToArray();30 //循环上述获取的非抽象类31 foreach (var ctrlType in controllerTypes)32 {33 //从namespace中提取版本号34 var match = Regex.Match(ctrlType.Namespace, @"_05_WebApiExtend.Controllers.v(\d+)");35 if (match.Success)36 {37 //获取版本号38 string verNum = match.Groups[1].Value;39 //获取控制器名称(eg:PersonController中获取Person)40 string controllerName = Regex.Match(ctrlType.Name, "(.+)Controller").Groups[1].Value;41 //声明集合中的键(形式:Personv1 、Personv2)42 string key = controllerName + "v" + verNum;43 dict[key] = new HttpControllerDescriptor(_config, controllerName, ctrlType);44 }45 46 }47 48 }49 return dict;50 }51 52 ///
53 /// 进行Controller的匹配54 /// 55 ///
56 ///
匹配成功返回控制器信息,匹配失败返回null
57 58 public override HttpControllerDescriptor SelectController(HttpRequestMessage request)59 {60 //获取所有的controller键值集合61 var controllers = GetControllerMapping();62 //获取路由数据63 var routeData = request.GetRouteData();64 //从路由中获取当前controller的名称65 var controllerName = (string)routeData.Values["controller"];66 67 //下面是两种方式获取版本号68 string verNum = "";69 try70 {71 //从报文头中获取版本号(当没有这个参数的时候走catch)72 verNum = request.Headers.GetValues("ApiVersion").Single();73 }74 catch (Exception)75 {76 //从url中获取版本号77 verNum = Regex.Match(request.RequestUri.PathAndQuery, @"api/v(\d+)").Groups[1].Value;78 }79 //拼接key值80 string key = controllerName + "v" + verNum;81 if (controllers.ContainsKey(key))82 {83 return controllers[key];84 }85 else86 {87 return null;88 }89 }90 }

3.  在WebConfig.cs类中,加上 config.Services.Replace(typeof(IHttpControllerSelector), new VersionConstrollerSelector(config)); ,即用重写IHttpControllerSelector的替换原先的IHttpControllerSelector,如下图:

 

4.  注释掉原先的路由请求模式,新增下面两个路由规则

1  public static class WebApiConfig 2     { 3         public static void Register(HttpConfiguration config) 4         { 5             // Web API 配置和服务 6             //用重写IHttpControllerSelector的替换原先的IHttpControllerSelector 7             config.Services.Replace(typeof(IHttpControllerSelector), new VersionConstrollerSelector(config)); 8             // Web API 路由 9             config.MapHttpAttributeRoutes();10             //config.Routes.MapHttpRoute(11             //    name: "DefaultApi",12             //    routeTemplate: "api/{controller}/{action}/{id}",13             //    defaults: new { id = RouteParameter.Optional }14             //);15 16             //多版本控制的路由改造17             config.Routes.MapHttpRoute(18               name: "DefaultApiV1",19               routeTemplate: "api/v1/{controller}/{action}/{id}",20               defaults: new { id = RouteParameter.Optional }21              );22 23             config.Routes.MapHttpRoute(24              name: "DefaultApiV2",25              routeTemplate: "api/v2/{controller}/{action}/{id}",26              defaults: new { id = RouteParameter.Optional }27             );28         }29     }

5. 用PostMan测试

(1). 分别请求 http://localhost:2182/api/v1/Person/GetName?Name=2   和 http://localhost:2182/api/v2/Person/GetName?Name=2, 返回不同的版本的信息,证明可以根据Url中的v1和v2进行版本区分。

(2). 请求 http://localhost:2182/api/v1/Person/GetName?Name=2 地址两次 ,表头中分别带有ApiVersion=1 和 ApiVersion=2,返回不同版本的信息,证明可以报文头中的参数进行版本区分。

 

 

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 :
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

转载于:https://www.cnblogs.com/yaopengfei/p/10508566.html

你可能感兴趣的文章
Linux平台下使用rman进行oracle数据库迁移
查看>>
全栈工程师学习Linux技术的忠告
查看>>
iOS自定制tabbar与系统的tabbar冲突,造成第一次点击各个item图片更换选中,第二次选中部分item图片不改变...
查看>>
C# Dictionary用法总结
查看>>
SVN服务器使用(二)
查看>>
反射获取内部类以及调用内部类方法
查看>>
C语言 - pthread
查看>>
谈Linq To Sql的优劣--纯个人观点
查看>>
HDU 4996 Revenge of LIS(DP)
查看>>
App里面如何正确显示用户头像
查看>>
DATAGUARD维护:从库宕机后如何恢复到管理恢复模式
查看>>
Android中的PID和UID
查看>>
MAC下上公司内网
查看>>
CentOS7.4安装mysql5.7
查看>>
U-BOOT之一:BootLoader 的概念与功能
查看>>
我的路上
查看>>
Velocity处理多余空白和多余空白行问题
查看>>
内容开发平台(PLATFORM)
查看>>
java值传递
查看>>
判断一个数是否为素数的一个讨论(一)
查看>>