WebAPI多版本控制实现的几种方式——自定义IHttpControllerSelector
这种方法的原理是通过分析url来区分版本。
1) WebApiConfig 中的路由改成如下:
使用这种方式的好处:以后每次新添加api版本只需要在webconfig配置中新增加一条路由配置,比如项目中新增了名为v3版本,只需要把
routeTemplate: "api/v2/{controller}/{id}"中的v2改成v3即可。当然如果需要改变路由的格式也可以,后面的配置格式也要按照改变的格式来。
// Web API 配置和服务 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/v1/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Routes.MapHttpRoute( name: "DefaultApi1", routeTemplate: "api/v2/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
2)不同版本的 Controller 放到不同的 namespace 下:
由于该方式是通过判断namespace名来选择版本,所以在Controllers文件下为每个版本创建新的文件夹,文件夹名就为路由配置中版本号格式,
API文件在放入对应的文件夹下。例如我的项目例子中:

APIDemoController (v1)代码:
namespace webapi.Controllers.v1 { public class APIDemoController : ApiController { public string Get(int id) { return "我是APIDemoV1" + id; } } }
APIDemoController(v2)代码
namespace webapi.Controllers.v2 { public class APIDemoController : ApiController { public string Get(int id) { return "我是APIDemoV2" + id; } { {
3)编写一个名为VersionControllerSelectord类(当然这个名字不是必须叫这个的):所有的实现都是在这个类中
public class VersionControllerSelector : DefaultHttpControllerSelector { private HttpConfiguration _config; IDictionary<string, HttpControllerDescriptor> ctlMapping; public VersionControllerSelector(HttpConfiguration config) : base(config) { _config = config; } /// <summary> /// 获得控制器下所有的动作 /// </summary> /// <returns></returns> public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { Dictionary<string, HttpControllerDescriptor> dict = new Dictionary<string, HttpControllerDescriptor>(); foreach (var asm in _config.Services.GetAssembliesResolver().GetAssemblies()) { //获取所有继承自ApiController的非抽象类 var controllerTypes = asm.GetTypes() .Where(t => !t.IsAbstract && typeof(ApiController).IsAssignableFrom(t)).ToArray(); foreach (var ctrlType in controllerTypes) { //在从namespace中提取出版本号,这里用的正则表达式来获取 var match = Regex.Match(ctrlType.Namespace, @"\.v(\d)"); if (match.Success) { string verName = match.Groups[1].Value; //拿到版本号 var matchController = Regex.Match(ctrlType.Name, @"^(.+)Controller$"); if (matchController.Success) { string ctrlName = matchController.Groups[1].Value; //拿到控制器的名字 string key = ctrlName + "v" + verName;//将控制器和版本号拼接起来 dict[key] = new HttpControllerDescriptor(_config, ctrlName, ctrlType); } } } } ctlMapping = dict; //因为这个方法里已经拿到了,所以把它缓存起来,给SelectController中使用 return dict; } /// <summary> /// 选择那个版本下的控制器 /// </summary> /// <param name="request"></param> /// <returns></returns> public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { //没有的话在调用一次 if(ctlMapping==null) { //获取所有Controller键值集合 ctlMapping = GetControllerMapping(); } //获取路由数据 var routeData = request.GetRouteData(); //从路由中获取当前Controller的名字 string controllerName = routeData.Values["controller"].ToString(); //从url中获得版本号 string verNum =Regex.Match(request.RequestUri.PathAndQuery, @"api/v(\d+)").Groups[1].Value; string key = controllerName + "v" + verNum; if (ctlMapping.ContainsKey(key)) { return ctlMapping[key]; }else { return null; } } }
4)在 WebApiConfig中添加
config.Services.Replace(typeof(IHttpControllerSelector),new VersionControllerSelector(config));
5)最后我们来测试写好的api


这个可以说是个一劳永逸的方法,新增版本后只需要新増一条webconfig中配置即可。

浙公网安备 33010602011771号