WebApi+Swagger实现多版本控制

效果图如下:

WebApi+Swagger实现多版本控制_第1张图片

 WebApi+Swagger实现多版本控制_第2张图片

打开 管理NuGet程序包 添加 Swashbuckle

WebApi+Swagger实现多版本控制_第3张图片

    public class CachingSwaggerProvider : ISwaggerProvider
    {
        private static ConcurrentDictionary _cache =
            new ConcurrentDictionary();

        private readonly ISwaggerProvider _swaggerProvider;

        public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
            _swaggerProvider = swaggerProvider;
        }

        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
            var cacheKey = String.Format("{0}_{1}", rootUrl, apiVersion);
            SwaggerDocument srcDoc = null;
            //只读取一次
            if (!_cache.TryGetValue(cacheKey, out srcDoc))
            {
                srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
                var patht = new Dictionary();
                foreach (var item in srcDoc.paths)
                {
                    var arr = item.Key.Split('/');
                    var i = arr[3].LastIndexOf('.') + 1;
                    if (i != -1)
                    {
                        arr[3] = arr[3].Substring(i);
                    }
                    patht.Add(string.Join("/", arr), item.Value);
                }
                srcDoc.paths = patht;
                HashSet moduleList = new HashSet();
                srcDoc.vendorExtensions = new Dictionary
                {
                    {"ControllerDesc", GetControllerDesc(moduleList)},
                    {"AreaDescription", moduleList}
                };
                _cache.TryAdd(cacheKey, srcDoc);
            }
            return srcDoc;
        }

        /// 
        /// 从API文档中读取控制器描述
        /// 
        /// 所有控制器描述
        public static ConcurrentDictionary GetControllerDesc(HashSet moduleList)
        {
            string xmlpath = String.Format("{0}/bin/BrnMall.Web.XML", AppDomain.CurrentDomain.BaseDirectory);
            ConcurrentDictionary controllerDescDict = new ConcurrentDictionary();
            if (File.Exists(xmlpath))
            {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(xmlpath);
                string type = String.Empty, path = String.Empty, controllerName = String.Empty;
                string[] arrPath;
                int length = -1, cCount = "Controller".Length;
                XmlNode summaryNode = null;
                foreach (XmlNode node in xmldoc.SelectNodes("//member"))
                {
                    type = node.Attributes["name"].Value;
                    if (type.StartsWith("T:"))
                    {
                        //控制器
                        arrPath = type.Split('.');
                        length = arrPath.Length;
                        controllerName = arrPath[length - 1];
                        if (controllerName.EndsWith("Controller"))
                        {
                            //模块信息
                            var moduleName = arrPath[length - 2];
                            moduleList.Add(moduleName);
                            //获取控制器注释
                            summaryNode = node.SelectSingleNode("summary");
                            string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                            if (summaryNode != null && !String.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                            {

                                controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                            }
                        }
                    }
                }
            }
            return controllerDescDict;
        }
    }

App_Start 文件夹下添加 CachingSwaggerProvider 类: 

SwaggerConfig类中添加如下配置:

配置多版本

public class SwaggerVersionHelper
{
    public static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
    {
       var attr = apiDesc.ActionDescriptor.ControllerDescriptor.GetCustomAttributes().FirstOrDefault();
       return attr.Version == Convert.ToInt32(targetApiVersion.TrimStart('v'));
    }
}


[AttributeUsage(AttributeTargets.All)]
public class VersionedRoute : Attribute
{
   public VersionedRoute(string name, int version)
   {
      Name = name;
      Version = version;
   }
   public string Name { get; set; }
   public int Version { get; set; }
}

 

c.MultipleApiVersions(
                            (apiDesc, targetApiVersion) => SwaggerVersionHelper.ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
                            (vc) =>
                            {
                                vc.Version("v1", "App接口V1");
                                vc.Version("v2", "App接口V2");
                                vc.Version("v3", "App接口V3");
                            });

配置返回参数

c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));

添加注释

string path = string.Format("{0}/bin/BrnMall.Core.XML", System.AppDomain.CurrentDomain.BaseDirectory);
                        c.IncludeXmlComments(path);
                        path = string.Format("{0}/bin/BrnMall.Web.XML", System.AppDomain.CurrentDomain.BaseDirectory);
                        c.IncludeXmlComments(path);
                        path = string.Format("{0}/bin/BrnMall.Services.XML", System.AppDomain.CurrentDomain.BaseDirectory);
                        c.IncludeXmlComments(path);

添加VersionControllerSelector 类用于实现多版控制器:

    public class VersionControllerSelector : IHttpControllerSelector
    {
        private const string VersionKey = "version";
        private const string ControllerKey = "controller";
        private readonly HttpConfiguration _configuration;
        private readonly Lazy> _controllers;
        private readonly HashSet _duplicates;
        public VersionControllerSelector(HttpConfiguration config)
        {
            _configuration = config;
            _duplicates = new HashSet(StringComparer.OrdinalIgnoreCase);
            _controllers = new Lazy>(InitializeControllerDictionary);
        }
        private Dictionary InitializeControllerDictionary()
        {
            var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
            IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
            IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
            ICollection 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);
                }
                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;
        }
        private static T GetRouteVariable(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);
            }

            string version = GetRouteVariable(routeData, VersionKey);
            if (string.IsNullOrEmpty(version))
            {
                version = GetVersionFromHTTPHeaderAndAcceptHeader(request);
            }
            string controllerName = GetRouteVariable(routeData, ControllerKey);
            if (controllerName == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            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 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;
        }
    }

WebApiConfig类中添加如下代码

config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));

测试代码如下:

namespace BrnMall.Web.Areas.Web.Controllers.v1
{
    [VersionedRoute("api/vrsion", 1)]
    public class TextController : BaseWebController
    {

    }
}

namespace BrnMall.Web.Areas.Web.Controllers.v2
{
    [VersionedRoute("api/vrsion", 2)]
    public class TextController : BaseWebController
    {

    }
}

namespace BrnMall.Web.Areas.Web.Controllers.v3
{
    [VersionedRoute("api/vrsion", 3)]
    public class TextController : BaseWebController
    {

    }
}

 

到此就大功告成!! 

你可能感兴趣的:(WebApi+Swagger实现多版本控制)