• WebApi的多版本管理


    1.多版本管理概念

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

    2.解决方式

        1.不同版本用不同的域名:v1.api.rsfy.comv2.api.rsfy.comv3……

        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方式来完成了多版本管理

  • 相关阅读:
    LF 第三章 装饰器和迭代器相关
    Python 文件管理
    Python 强制类型转换
    安装模块
    LF 第三章
    pep8 Python编码规则
    Help on module pyclbr:
    Help on class timedelta in module datetime:
    Help on function meshgrid in module numpy.lib.function_base:
    Help on module matplotlib.cm in matplotlib:
  • 原文地址:https://www.cnblogs.com/yan7/p/7857342.html
Copyright © 2020-2023  润新知