zoukankan      html  css  js  c++  java
  • WebApi的Swagger多版本控制实现

    WebApi + Swagger2.0接口文档多版本控制设计实现

    最近前后端分离的项目越来越多,API的对接对于前后端开发交流得最多的一块内容,一个好的API文档生成工具就显得非常重要,选取了Swagger文档生成工具作为项目的文档生成工具,考虑到多版本的文档生成,基于swagger5.6版本实现了一套多版本控制

    1.  NuGet包引用

    比较简单,下面三个包都引用,删掉自动生成的页面文件,本此介绍多版本此处省略NuGet引入的文件清理工作了

    2. 多版本控制

    找到App_Start文件夹下面的SwaggerConfig,找到下图的节点,默认是c.SingApiVersion生效,先注释此行,再打开c.MultipleApiVersions勾子节点

    此处有一个匿名方法调用ResolveVersionSupportByRouteConstraint,此方法需要自己实现,其中apiDesc是当前工程文件实现ApiController的全部路由数据,targetApiVersion是当前版本信息,直接在SwaggerConfig类中生成私有方法,我的实现如下,思路是在此处过滤掉非当前版本的Controller路由

     private static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
            {
                //过滤由多版本的controller带来的重复route注册api desc,按命名空间的版本信息过滤,只返回版本内的api
                return apiDesc.ActionDescriptor.ControllerDescriptor.ControllerType.FullName.ToLower().Contains(string.Format(".{0}.", targetApiVersion));
            }

    3. 路由文件版本分离规则

    注意工程文件中Controllers的版本是通过命名空间隔离的,上面的代码就是通过版本信息来匹配这个命名空间来实现过滤,假设v1和v2文件夹中都有ValuesController

    两个版本的ValuesController如下,route在Map时不能有重复名称的Controller,所以此处采取加上版本,v1不需要添加,v2的ValuesController上加上版本, 借助RoutePrefix为ValuesV2Controller重写路由,对外路由统一为api/{verstion}/Values并不会有ValuesV2出现在api路由上,此处的ValuesV2在文档生成时还需要特殊处理,下面会重点提到IDocumentFilter接口实现时的处理

    4. 多版本路由注册

    此时在WebApiConfig的路由注册中写下如下代码,分别注册两个版本的路由对应v1,v2版本

    config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/v1/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional}
                );
    
                config.Routes.MapHttpRoute(
                    name: "Apiv2",
                    routeTemplate: "api/v2/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                    );

    5. IDocumentFilter接口实现

    多版本的控制到这就算基本实现了,只不过ValuesV2这个路由的文档中依然会带上这个V2, 由于我们在RoutePrefix中已经配置了V2的Values路由是api/v2/Values,但按路由注册规则文档生成的路由默认会是api/v2/ValuesV2, 如果按文档生成的路径调用时会报找不到文件,原因就是RoutePrefix生效的路由与Swagger文档路由不匹配,好在Swagger提供的过滤接口能帮助我们自定义过滤规则,在SwaggerConfig中找到如下节点,打开注释,其中ApplyDocumentVendorExtensions需要我们自己实现

    c.DocumentFilter<ApplyDocumentVendorExtensions>();

    实现IDocumentFilter接口的ApplyDocumentVendorExtensions文档处理类

    internal class ApplyDocumentVendorExtensions : IDocumentFilter
        {
            /// <summary>
            /// //swagger版本控制过滤
            /// </summary>
            /// <param name="swaggerDoc">文档</param>
            /// <param name="schemaRegistry">schema注册</param>
            /// <param name="apiExplorer">api概览</param>
            public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
            {
                //缓存目标路由api
                IDictionary<string, PathItem> match = new Dictionary<string, PathItem>();
                //取版本
                var version = swaggerDoc.info.version;
                foreach (var path in swaggerDoc.paths)
                {
                    //过滤命名空间 按名称空间区分版本
                    if (path.Key.Contains(string.Format("/{0}/", version)))
                    {
                        //匹配controller descript中的版本信息
                        Regex r = new Regex("/\w+" + version, RegexOptions.IgnoreCase);
                        string newKey = path.Key;
                        if (r.IsMatch(path.Key)) {
                            var routeinfo = r.Match(path.Key).Value;
                            //修正controller别名路由符合RoutePrefix配置的路由 如api/v2/ValuesV2 修正为 api/v2/Values
                            newKey = path.Key.Replace(routeinfo, routeinfo.Replace(version.ToLower(), "")).Replace(
                                routeinfo, routeinfo.Replace(version.ToUpper(), ""));
                        }
                        //保存修正的path
                        match.Add(newKey, path.Value);
                    }
                }
                //当前版本的swagger document
                swaggerDoc.paths = match;
            }
        }

    至此多版本控制就实现了,其中要注意的地方是Controllers下的V1,V2文件夹下的Controller文件的命名空间要保持默认命名空间,效果如下

    ValuesV2请求的实际路由已修正

    有部分朋友询问SwaggerConfig中的勾子方法使用,本文重点在搭建SwaggerConfig在线文档和多版本实现,勾子方法推荐使用的几个方法下面简单介绍下:

    开启数据模型注释在文档中生成,参数为当前应用程序依赖的数据模型程序集名称,GetXmlPath自己实现

    c.IncludeXmlComments(GetXmlPath("DataModel"));

    //控制器描述Provider,Swagger提供了ISwaggerProvider接口,供自定义实现控制器的描述在文档中的生成,CachingSwaggerProvider需要自己实现ISwaggerProvider接口

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

    //Action相关描述

    c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());

    Swagger接口文档页面中的label汉化js文件,来源网络

    c.InjectJavaScript(thisAssembly, "WebApi.Scripts.Swagger-Custom.js");

    有可运行的demo没找到地方上传,欢迎索取

                                                                                --晴天的故事

  • 相关阅读:
    RestKit ,一个用于更好支持RESTful风格服务器接口的iOS库
    Pop–实现任意iOS对象的任意属性的动态变化
    界面传值失败
    UIImagePickerController
    NSURLSession
    iOS图标尺寸
    cocoapods
    duplicate symbol _OBJC_IVAR
    MAC升级openssl
    Mac
  • 原文地址:https://www.cnblogs.com/johnx/p/10150671.html
Copyright © 2011-2022 走看看