zoukankan      html  css  js  c++  java
  • 电商系统架构总结4(webapi 版本控制)

        为了 顺利迭代升级,web api 在维护过程是不断升级的,但用户是不能强迫他们每次都跟随你去升级,这样会让用户不胜其烦。为了保证不同版本的客户端能同时兼容,在web api接口上加入版本控制就很有必要了。

    当然,对于我们开发的代码进行版本控制也有利,不至于陷入混乱。版本参数可以放置在请求的url 作为路由参数的一部分,也可以放在header里。实现的办法是 实现 IHttpControllerSelector 并在WebApiConfig的注册方法里进行替换。

        public class VersionHttpControllerSelector : IHttpControllerSelector
        {
            private const string VersionKey = "version";
            private const string ControllerKey = "controller";
    
            private readonly HttpConfiguration _configuration;
            private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
            private readonly HashSet<string> _duplicates;
    
            public VersionHttpControllerSelector(HttpConfiguration config)
            {
                _configuration = config;
                _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
                _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
            }
    
            private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
            {
                var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
                IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
                IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
                ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
    
                foreach (Type t in controllerTypes)
                {
                    var segments = t.Namespace.Split(Type.Delimiter);
    
                    var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
                    string version = segments[segments.Length - 1];
                    var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName);
                    if (version == "Controllers")
                    {
                        key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
                    }
                    // Check for duplicate keys.
                    if (dictionary.Keys.Contains(key))
                    {
                        _duplicates.Add(key);
                    }
                    else
                    {
                        dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
                    }
                }
                foreach (string s in _duplicates)
                {
                    dictionary.Remove(s);
                }
                return dictionary;
            }
    
            // Get a value from the route data, if present.
            private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
            {
                object result = null;
                if (routeData.Values.TryGetValue(name, out result))
                {
                    return (T)result;
                }
                return default(T);
            }
    
            public HttpControllerDescriptor SelectController(HttpRequestMessage request)
            {
                IHttpRouteData routeData = request.GetRouteData();
                if (routeData == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
    
                // Get the version and controller variables from the route data.
                string version = GetRouteVariable<string>(routeData, VersionKey);
                if (string.IsNullOrEmpty(version))
                {
                    version = GetVersionFromHTTPHeaderAndAcceptHeader(request);
                }
                string controllerName = GetRouteVariable<string>(routeData, ControllerKey);
                if (controllerName == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
    
                // Find a matching controller.
                string key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
                if (!string.IsNullOrEmpty(version))
                {
                    key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName);
                }
    
                HttpControllerDescriptor controllerDescriptor;
                if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
                {
                    return controllerDescriptor;
                }
                else if (_duplicates.Contains(key))
                {
                    throw new HttpResponseException(
                        request.CreateErrorResponse(HttpStatusCode.InternalServerError,
                        "Multiple controllers were found that match this request."));
                }
                else
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
            }
    
            public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
            {
                return _controllers.Value;
            }
            private string GetVersionFromHTTPHeaderAndAcceptHeader(HttpRequestMessage request)
            {
                if (request.Headers.Contains(VersionKey))
                {
                    var versionHeader = request.Headers.GetValues(VersionKey).FirstOrDefault();
                    if (versionHeader != null)
                    {
                        return versionHeader;
                    }
                }
                var acceptHeader = request.Headers.Accept;
                foreach (var mime in acceptHeader)
                {
                    if (mime.MediaType == "application/json" || mime.MediaType == "text/html")
                    {
                        var version = mime.Parameters
                                         .Where(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase))
                                          .FirstOrDefault();
    
                        if (version != null)
                        {
                            return version.Value;
                        }
                        return string.Empty;
                    }
                }
                return string.Empty;
            }
        }
    View Code

    重点是SelectController方法,从http请求里找出合适版本的controller。我这里兼容了从路由和header里获取版本,先从路由里获取,没有再从header里获取。

               IHttpRouteData routeData = request.GetRouteData();
                if (routeData == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
    
                // Get the version and controller variables from the route data.
                string version = GetRouteVariable<string>(routeData, VersionKey);
                if (string.IsNullOrEmpty(version))
                {
                    version = GetVersionFromHTTPHeaderAndAcceptHeader(request);
                }
          private string GetVersionFromHTTPHeaderAndAcceptHeader(HttpRequestMessage request)
            {
                if (request.Headers.Contains(VersionKey))
                {
                    var versionHeader = request.Headers.GetValues(VersionKey).FirstOrDefault();
                    if (versionHeader != null)
                    {
                        return versionHeader;
                    }
                }
                var acceptHeader = request.Headers.Accept;
                foreach (var mime in acceptHeader)
                {
                    if (mime.MediaType == "application/json" || mime.MediaType == "text/html")
                    {
                        var version = mime.Parameters
                                         .Where(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase))
                                          .FirstOrDefault();
    
                        if (version != null)
                        {
                            return version.Value;
                        }
                        return string.Empty;
                    }
                }
                return string.Empty;
            }

    WebApiConfig文件调用代码如下:

       public static void Register(HttpConfiguration config)
            {
    。。。
    config.Services.Replace(typeof(IHttpControllerSelector), new VersionHttpControllerSelector((config)));
    
    }

    web api的定义呢,则从命名空间上区分就可以了。 比如版本号为V1的  LoginApiController 的命名空间 为定义为    xxx.WebAPI.Controllers.V1,版本号为V2的  LoginApiController 的命名空间 为定义为    xxx.WebAPI.Controllers.V2,如此类推,

    客户端在header里加上参数 versoin=v1/v2... 就可以指定使用不同版本的api了。

  • 相关阅读:
    重打技术征集系统第七稿开发第1、2天
    关于centos7右上角网络图标消失的解决办法
    记录一下hbase踩的坑
    在jsp中,单选按钮的点击事件(点击隐藏或显示输入框)
    input中禁止输入特殊字符
    《软件方法》阅读笔记——1
    随手快递app开发第十七天
    Codeforces Round #284 (Div. 1) B. Name That Tune(最大流)
    hihocoder 1310岛屿(dfs,一个搜索技巧)
    Codeforces Round #294 (Div. 2) E. A and B and Lecture Rooms(lca+思维,树上寻找与给定两个点距离相等的点的个数)
  • 原文地址:https://www.cnblogs.com/lindping/p/9028838.html
Copyright © 2011-2022 走看看