第十一节:WebApi的版本管理的几种方式

一. 背景和方案

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等做反向代理服务,然后将 http://api.ypf.com/api/v1/User/1和http://api.ypf.com/api/v2/User/1 转到不同的服务器处理。

(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     /// <summary>2     /// 自己实现IHttpControllerSelector接口来替换系统默认的IHttpControllerSelector3     /// </summary>4     public class VersionConstrollerSelector : DefaultHttpControllerSelector5     {6 7         private HttpConfiguration _config;8         /// <summary>9         /// 构造函数
10         /// </summary>
11         /// <param name="config"></param>
12         public VersionConstrollerSelector(HttpConfiguration config) : base(config)
13         {
14             _config = config;
15         }
16 
17 
18         /// <summary>
19         /// 获取所有的Controller
20         /// </summary>
21         /// <returns></returns>
22         public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
23         {
24             Dictionary<string, HttpControllerDescriptor> dict = new Dictionary<string, HttpControllerDescriptor>();
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         /// <summary>
53         /// 进行Controller的匹配
54         /// </summary>
55         /// <param name="request"></param>
56         /// <returns>匹配成功返回控制器信息,匹配失败返回null</returns>
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             try
70             {
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             else
86             {
87                 return null;
88             }
89         }
90     }

复制代码

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

 

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

复制代码

 1  public static class WebApiConfig2     {3         public static void Register(HttpConfiguration config)4         {5             // Web API 配置和服务6             //用重写IHttpControllerSelector的替换原先的IHttpControllerSelector7             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,返回不同版本的信息,证明可以报文头中的参数进行版本区分。

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类加载器分类

类加载器干就是将对应类的.class文件中的二进制流加载到内存空间,只管加载&#xff0c;只要符合文件结构就加载&#xff0c;至于能否运行&#xff0c;它不负责 类加载器的分类 1.启动类加载器 2.扩展类加载器 3.应用程序类加载器 委托机制:双亲委派模型 当一个类加载的过…

第十二节:WebApi自动生成在线Api文档的两种方式

一. WebApi自带生成api文档 1. 说明 通过观察&#xff0c;发现WebApi项目中Area文件夹下有一个HelpPage文件夹&#xff0c;如下图&#xff0c;该文件夹就是WebApi自带的生成Api的方式&#xff0c;如果该文件夹没了&#xff0c;可以通过Nuget安装&#xff1a;Microsoft.AspNet.…

JVM 组成部分

JVM可以分为3大部分&#xff1a;类加载器&#xff0c;运行时数据区和执行引擎。 类加载器负责加载.class 文件 运行时数据区负责存放.class 文件&#xff0c;分配内存。运行时数据区又分为5个部分: 方法区&#xff1a;负责存放.class 文件&#xff0c;方法区里有一块区域是运…

第十三节:Asp.Net Core WebApi基础总结和请求方式-第十八节

一. 基础总结 1.Restful服务改造 Core下的WebApi默认也是Restful格式服务&#xff0c;即通过请求方式(Get,post,put,delete)来区分请求哪个方法&#xff0c;请求的URL中不需要写方法名。 但是我们不喜欢这种方式&#xff0c;所以我们将默认的路由规则 [Route("api/[contr…

第十四节:Asp.Net Core WebApi生成在线文档-第十九节

一. 基本概念 1.背景 使用 Web API 时&#xff0c;了解其各种方法对开发人员来说可能是一项挑战。 Swagger 也称为OpenAPI&#xff0c;解决了为 Web API 生成有用文档和帮助页的问题。 它具有诸如交互式文档、客户端 SDK生成和 API 可发现性等优点&#xff0c;目前有两种实现方…

第十五节:Asp.Net Core MVC和WebApi路由规则的总结和对比-第二十节

一. Core Mvc 1.传统路由 Core MVC中&#xff0c;默认会在 Startup类→Configure方法→UseMvc方法中&#xff0c;会有默认路由&#xff1a;routes.MapRoute("default", "{controllerHome}/{actionIndex}/{id?}"); 等价于 app.UseMvcWithDefaultRoute(); …

String、StringBuffer和StringBuilde的区别

StringBuffer 和 StringBuilder 类提供了操作字符串的方法。 String为字符串常量&#xff0c;即String对象一旦创建之后该对象是不可更改&#xff0c;final 修饰&#xff0c;可以被不同线程共享&#xff0c;是线程安全的。在涉及多线程操作中不需要同步操作。 StringBuilder和…

第四节:Task的启动的四种方式以及Task、TaskFactory的线程等待和线程延续的解决方案

一. 背景 揭秘&#xff1a; 在前面的章节介绍过&#xff0c;Task出现之前&#xff0c;微软的多线程处理方式有&#xff1a;Thread→ThreadPool→委托的异步调用&#xff0c;虽然也可以基本业务需要的多线程场景&#xff0c;但它们在多个线程的等待处理方面、资源占用方面、线程…

Java中对字符串的操作

字符串反转 StringBuffer 或 StringBuilder 的 reverse方法 String s1 new StringBuilder(str).reverse().toString(); String s3 new StringBuffer(str).reverse().toString(); System.out.println(s1);//cba System.out.println(s3);//cbaList集合拼接成逗号分隔的字符串…

第五节:Task构造函数之TaskCreationOptions枚举处理父子线程之间的关系。

一. 整体说明 揭秘&#xff1a; 通过F12查看Task类的源码(详见下面的截图)&#xff0c;发现Task类的构造函数有有一个参数为&#xff1a;TaskCreationOptions类型&#xff0c;本章节可以算作是一个扩展章节&#xff0c;主要就来研究TaskCreationOptions类的作用。 该类主要用来…

List,Map,实体类,字符串相互转换

添加依赖 <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.7</version></dependency> List,实体类字符串想换转换 package Map;import com.alibaba.fastjson.*; import dto.Pers…

第六节:深入研究Task实例方法ContinueWith的参数TaskContinuationOptions

一. 整体说明 揭秘&#xff1a; 该章节的性质和上一个章节类似&#xff0c;也是一个扩展的章节&#xff0c;主要来研究Task类下的实例方法ContinueWith中的参数TaskContinuationOptions。 通过F12查看TaskContinuationOptions的源码&#xff0c;知道主要有这么几个参数&#xf…

java8 stream流操作集合交集,差集,并集,过滤,分组,去重,排序,聚合等

测试对象 public class Person {private String name;private Integer age;private Integer weight;public Person() {}public Integer getWeight() {return weight;}public void setWeight(Integer weight) {this.weight weight;}public Integer getAge() {return age…

第七节:利用CancellationTokenSource实现任务取消和利用CancellationToken类检测取消异常。

一. 传统的线程取消 所谓的线程取消&#xff0c;就是线程正在执行的过程中取消线程任务。 传统的线程取消&#xff0c;是通过一个变量来控制&#xff0c;但是这种方式&#xff0c;在release模式下&#xff0c;被优化从cpu高速缓存中读取&#xff0c;而不是从内存中读取&#xf…

start()和run()的区别

start方法&#xff1a; 通过该方法启动线程的同时也创建了一个线程&#xff0c;真正实现了多线程。无需等待run()方法中的代码执行完毕&#xff0c;就可以接着执行下面的代码。此时start()的这个线程处于就绪状态&#xff0c;当得到CPU的时间片后就会执行其中的run()方法。这个…

c#中的BeginInvoke和EndEndInvoke 摘要

摘要 异步这东西&#xff0c;真正用起来的时候&#xff0c;发现事情还是挺多的&#xff0c;最近在项目中用到了异步的知识&#xff0c;发现对它还是不了解&#xff0c;处理起来&#xff0c;走了不少弯路。觉得还是补一补还是很有必要的。 MSDN原文地址&#xff1a;https://ms…

关于@DateTimeFormat 和 @JsonFormat 注解

两个参数都是针对日期格式化做处理 1.入参格式化DateTimeFormat 传入参数是 String 类型,接收的参数Date 类型&#xff0c;类型无法转换。 使用 Spring 的 DateTimeFormat 注解格式化参数 传入参数要是日期格式的String 类型 例如:"2021-10-01" pattern &quo…

NET 提供了执行异步操作的三种模式

1.APM模式简介 在.net1.x的版本中就可以使用IAsyncResult接口实现异步操作&#xff0c;但是比较复杂&#xff0c;这种称之为异步编程模型模式 (Asynchronous Programming Model, APM)&#xff0c;也称为IAsyncResult模式 在这种APM模式下&#xff0c;一个同步操作XXX需要定义B…

java实体类属性非空判断工具类

import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry;public class CheckParametersUtil {Map<String, Object> map new HashMap<>();/*** 添加需要校验的参数* param object 实参* param parameterName 参…

第八节:Task的各类TaskTResult返回值以及通用线程的异常处理方案。

一. Task的各种返回值-Task<TResult> PS&#xff1a; 在前面章节&#xff0c;我们介绍了Task类开启线程、线程等待、线程延续的方式&#xff0c;但我们并没有关注这些方式的返回值&#xff0c;其实他们都是有返回值的Task<TResult>&#xff0c;然后可以通过Task的…