WebApi的多版本管理

时间:2022-04-19 18:15:15

1.多版本管理概念

什么是API的多版本问题?Android等App存在着多版本客户端共存的问题:由于早期没有内置升级机制,用户不会升级,拒绝升级等原因,造成了许多软件的旧版本App也在运行。开发新版本App时,要给接口增加新的功能或者修改以前接口的规范,会造成旧版本App无法使用,因此再一定情况下会“保留旧接口的运行,新功能用新接口”,这样就会存在多版本接口共存的问题。

2.解决方式

1.不同版本用不同的域名:v1.api.rsfy.com、v2.api.rsfy.com、v3……;

2.在Url,报文头等中带不同的版本信息,用Nginx等做反向代理服务,然后将 http://api.rsfy.com/api/v1/User/1http://api.rsfy.com/api/v2/User/1 转到不同的服务器处理

3.多个版本的Controller共处在一个项目中,然后使用[RoutePrefix]或者IHttpControllerSelector根据报文头,路径等选择不同的Controller执行

下面以第三个种记录一个例子

3.解决例题

创建一个WebApi项目,在Controllers中创建各个版本的目录

WebApi的多版本管理

然后我们在每个版本下创建一个Home控制器

WebApi的多版本管理

  public class HomeController : ApiController
{
[HttpGet]
public String GetIndex()
{
return "这是v1版本的Index";
}
}
public class HomeController : ApiController
{
[HttpGet]
public String GetIndex()
{
return "这是v2版本的Index";
}
}

正常情况下,我们是不可以在Controllers中创建目录的,这不符合约定,所以我们必须改写其中代码,让其根据我们需求来选择控制器。

下面我们创建一个我们自己的IHttpControllerSelector的实现类来替换默认的IHttpControllerSelector。

 /// <summary>
/// 自己实现IHttpControllerSelector来替换默认IHttpConllerSelector
/// </summary>
public class VersionConstrollerSelector : IHttpControllerSelector
{
private readonly HttpConfiguration _conf;
public VersionConstrollerSelector(HttpConfiguration configuration)
{
_conf = configuration;
} public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
throw new NotImplementedException();
} public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
throw new NotImplementedException();
}
}

IHttpControllerSelector接口有两个方法,

GetControllerMapping():获取程序中所有的Api接口

SelectController(HttpRequestMessage request):匹配请求的路由

下面我们来重写这两个方法

        /// <summary>
/// 获取所有Controller
/// </summary>
/// <returns></returns>
public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
Dictionary<String, HttpControllerDescriptor> dict = new Dictionary<string, HttpControllerDescriptor>();
foreach (var item in _conf.Services.GetAssembliesResolver().GetAssemblies())
{//循环所有程序集
//获取所有继承自ApiController的非抽象类
var controllerTypes = item.GetTypes()
.Where(y => !y.IsAbstract && typeof(ApiController)
.IsAssignableFrom(y)).ToArray();
foreach (var ctrlType in controllerTypes)
{//循环程序集中类型
//从namespace中提取出版本号
var match = Regex.Match(ctrlType.Namespace,GetType().Namespace+ @".Controllers.v(\d+)");
if(match.Success)
{//匹配成功
//获取版本号
string verNum = match.Groups[].Value;
//从控制器总名称中拿到控制器名称(例: HomeController中获取Home)
string ctrlName = Regex.Match(ctrlType.Name, "(.+)Controller").Groups[].Value;
//声明集合中的键
String key = (ctrlName + "v" + verNum).ToLower();
//存储集合值(控制器信息)
dict[key] = new HttpControllerDescriptor(_conf, ctrlName, ctrlType);
}
}
}
return dict;
}
/// <summary>
/// 进行匹配Controller
/// </summary>
/// <param name="request">http请求信息</param>
/// <returns>匹配成功返回控制器信息,匹配失败返回null</returns>
public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
//获取所有的Controller集合
var controllers = GetControllerMapping();
//获取路由数据
var routeData = request.GetRouteData();
//从路由中获取当前controller的名称
var controllerName = routeData.Values["Controller"] as String;
//如果请求头中存在ApiVerson信息则总其中获取版本号否则从url中获取版本号
var verNum = request.Headers.TryGetValues("ApiVerson", out var versions) ?
versions.Single() :
Regex.Match(request.RequestUri.PathAndQuery, @"api/v(\d+)").Groups[].Value; //获取版本号
var key = (controllerName + "v" + verNum).ToLower();//获取Personv2
//返回控制器信息
return controllers.ContainsKey(key) ? controllers[key] : null; }

  现在我们这个类实现完成以后我们便可以在WebApiConfig类中的Register方法中替换原来的IHttpControllerSelector

  public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服务
//替换HttpControllerSelector
config.Services.Replace(typeof(IHttpControllerSelector), new VersionConstrollerSelector(config)); }
}

并且在其方法创建新的路由

public static void Register(HttpConfiguration config)
{// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApiv1",
routeTemplate: "api/v1/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }); config.Routes.MapHttpRoute(
name: "DefaultApiv2",
routeTemplate: "api/v2/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApiv3",
routeTemplate: "api/v3/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

至此,我们便成功的以替换IHttpControllerSelector方式来完成了多版本管理

WebApi的多版本管理

WebApi的多版本管理