WebApi的多版本管理

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中创建各个版本的目录

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

  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[1].Value;
                        //从控制器总名称中拿到控制器名称(例:  HomeController中获取Home)
                        string ctrlName = Regex.Match(ctrlType.Name, "(.+)Controller").Groups[1].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[1].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方式来完成了多版本管理

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏游戏杂谈

C# StreamReader.ReadLine统计行数的问题

从 lua 文件中提取字符串放到 excel 中,再将 excel 给海外同事,翻译完成后,用翻译的文本替换相应中文。

11510
来自专栏大内老A

通过扩展让ASP.NET Web API支持JSONP

同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝。同源策略以及跨域...

19470
来自专栏DOTNET

学会WCF之试错法——客户端调用基础

1当客户端调用未返回结果时,服务不可用(网络连接中断,服务关闭,服务崩溃等) 客户端抛出异常 异常类型:CommunicationException Inne...

28080
来自专栏有趣的django

CRM客户关系管理系统(十三) 第十三章、用户自定义认证第十四章、万能通用权限框架设计

43600
来自专栏大内老A

ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则

对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象,在不同的使用场景中可能具有不同的验证规则。举个简...

184100
来自专栏跟着阿笨一起玩NET

C# 解析js方法,并调用js方法

本文转载:http://www.cnblogs.com/StudyLife/archive/2013/03/11/2953516.html

43830
来自专栏大内老A

WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]

在《上篇》中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭。对于《WCF技术剖析(卷1)》的读者,应该会知道在第7章中我通...

20750
来自专栏WebApiClient

WebApiClient基础

如果接口IMyWebApi有多个方法且都指向同一服务器,可以将请求的域名抽出来放到HttpHost特性。

62300
来自专栏图像识别与深度学习

《HTML5实战》Lesson10

Week11  2016/11/23上午1-4节 一、复习 ? 对应的html ? 二、execCommand实现富文本编辑控件 1、execCommand ...

35450
来自专栏DOTNET

ASP.NET MVC编程——控制器

每一个请求都会经过控制器处理,控制器中的每个方法被称为控制器操作,它处理具体的请求。 1操作输入参数 控制器的操作的输入参数可以是内置类型也可以是自定义类型。 ...

28490

扫码关注云+社区

领取腾讯云代金券