• .net core的Swagger接口文档使用教程(二):NSwag


      上一篇介绍了Swashbuckle ,地址:.net core的Swagger接口文档使用教程(一):Swashbuckle

      讲的东西还挺多,怎奈微软还推荐了一个NSwag,那就继续写吧!

      但是和Swashbuckle一样,如果还是按照那样写,东西有点多了,所以这里就偷个懒吧,和Swashbuckle对照的去写,介绍一些常用的东西算了,所以建议看完上一篇再继续这里。

      

      一、一般用法

      注:这里一般用法的Demo源码已上传到百度云:https://pan.baidu.com/s/1Z4Z9H9nto_CbNiAZIxpFFQ (提取码:pa8s ),下面第二、三部分的功能可在Demo源码基础上去尝试。

      创建一个.net core项目(这里采用的是.net core3.1),然后使用nuget安装NSwag.AspNetCore,建议安装最新版本。

      同样的,假如有一个接口:  

        /// <summary>
        /// 测试接口
        /// </summary>
        [ApiController]
        [Route("[controller]")]
        public class HomeController : ControllerBase
        {
            /// <summary>
            /// Hello World
            /// </summary>
            /// <returns>输出Hello World</returns>
            [HttpGet]
            public string Get()
            {
                return "Hello World";
            }
        }

      接口修改Startup,在ConfigureServices和Configure方法中添加服务和中间件  

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOpenApiDocument(settings =>
            {
                settings.DocumentName = "v1";
                settings.Version = "v0.0.1";
                settings.Title = "测试接口项目";
                settings.Description = "接口文档说明";
            });
    
            ...
        }
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
          ... app.UseOpenApi(); app.UseSwaggerUi3(); ... }

      然后运行项目,输入http://localhost:5000/swagger,得到接口文档页面:

      

       点击Try it out可以直接调用接口。

       同样的,这里的接口没有注解,不太友好,可以和Swashbuckle一样生成xml注释文件加载:

      右键项目=》切换到生成(Build),在最下面输出输出中勾选【XML文档文件】,同时,在错误警告的取消显示警告中添加1591代码:

      

       不过,与Swashbuckle不一样的是,Swashbuckle需要使用IncludeXmlComments方法加载注释文件,如果注释文件不存在,IncludeXmlComments方法还会抛出异常,但是NSwag不需要手动加载,默认xml注释文件和它对应点dll应该放在同一目录且同名才能完成加载!

       按照上面的操作,运行项目后,接口就有注解了:

      

       但是控制器标签栏还是没有注解,这是因为NSwag的控制器标签默认从OpenApiTagAttribute中读取   

        [OpenApiTag("测试标签",Description = "测试接口")]
        public class HomeController : ControllerBase

      运行后显示:

      

        其实还可以修改这个默认行为,settings有一个UseControllerSummaryAsTagDescription属性,将它设置成 true就可以从xml注释文件中加载描述了:  

        services.AddOpenApiDocument(settings =>
        {
            ...
    
            //可以设置从注释文件加载,但是加载的内容可被OpenApiTagAttribute特性覆盖
            settings.UseControllerSummaryAsTagDescription = true;
        });

      运行后显示:

      

        接着是认证,比如JwtBearer认证,这个和Swashbuckle是类似的,只不过拓展方法换成了AddSecurity:  

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOpenApiDocument(settings =>
            {
                settings.DocumentName = "v1";
                settings.Version = "v0.0.1";
                settings.Title = "测试接口项目";
                settings.Description = "接口文档说明";
    
                //可以设置从注释文件加载,但是加载的内容可悲OpenApiTagAttribute特性覆盖
                settings.UseControllerSummaryAsTagDescription = true;
    
                //定义JwtBearer认证方式一
                settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
                {
                    Description = "这是方式一(直接在输入框中输入认证信息,不需要在开头添加Bearer)",
                    Name = "Authorization",//jwt默认的参数名称
                    In = OpenApiSecurityApiKeyLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Type = OpenApiSecuritySchemeType.Http,
                    Scheme = "bearer"
                });
    
                //定义JwtBearer认证方式二
                settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
                {
                    Description = "这是方式二(JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格))",
                    Name = "Authorization",//jwt默认的参数名称
                    In = OpenApiSecurityApiKeyLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Type = OpenApiSecuritySchemeType.ApiKey
                });
            });
    
            ...
        }

      到这里,就是NSwag的一般用法了,可以满足一般的需求了。

      二、服务注入(AddOpenApiDocument和AddSwaggerDocument)

      NSwag注入服务有两个方法:AddOpenApiDocument和AddSwaggerDocument,两者的区别就是架构类型不一样,AddOpenApiDocument的SchemaType使用的是OpenApi3,AddSwaggerDocument的SchemaType使用的是Swagger2:  

        /// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
        /// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
        /// <param name="configure">Configure the document.</param>
        public static IServiceCollection AddOpenApiDocument(this IServiceCollection serviceCollection, Action<AspNetCoreOpenApiDocumentGeneratorSettings, IServiceProvider> configure = null)
        {
            return AddSwaggerDocument(serviceCollection, (settings, services) =>
            {
                settings.SchemaType = SchemaType.OpenApi3;
                configure?.Invoke(settings, services);
            });
        }
        /// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
        /// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
        /// <param name="configure">Configure the document.</param>
        public static IServiceCollection AddSwaggerDocument(this IServiceCollection serviceCollection, Action<AspNetCoreOpenApiDocumentGeneratorSettings, IServiceProvider> configure = null)
        {
            serviceCollection.AddSingleton(services =>
            {
                var settings = new AspNetCoreOpenApiDocumentGeneratorSettings
                {
                    SchemaType = SchemaType.Swagger2,
                };
    
                configure?.Invoke(settings, services);
    
                ...
            });
    
            ...
        }

      个人推荐使用AddOpenApiDocument。  

        services.AddOpenApiDocument(settings =>
        {
            //添加代码
        });

      同样的,无论是AddOpenApiDocument还是AddSwaggerDocument,最终都是依赖AspNetCoreOpenApiDocumentGeneratorSettings来完成,与Swashbuckle不同的是,AddOpenApiDocument方法每次调用只会生成一个swagger接口文档对象,从上面的例子也能看出来:

      DocumentName

      接口文档名,也就是Swashbuckle中SwaggerDoc方法中的name参数。

      Version

      接口文档版本,也就是Swashbuckle中SwaggerDoc方法中的第二个OpenApiInfo的Version属性。

      Title

      接口项目名称,也就是Swashbuckle中SwaggerDoc方法中的第二个OpenApiInfo的Title属性。

      Description

      接口项目介绍,也就是Swashbuckle中SwaggerDoc方法中的第二个OpenApiInfo的Description属性。

      PostProcess

      这个是一个委托,在生成SwaggerDocument之后执行,需要注意的是,因为NSwag有缓存机制的存在PostProcess可能只会执行一遍

      比如:因为NSwag没有直接提供Swashbuckle中SwaggerDoc方法中的第二个OpenApiInfo的Contact属性的配置,这时我们可以使用PostProcess实现。  

        settings.PostProcess = document =>
        {
            document.Info.Contact = new OpenApiContact()
            {
                Name = "zhangsan",
                Email = "xxx@qq.com",
                Url = null
            };
        };

       ApiGroupNames

       无论是Swashbuckle还是NSwag都支持生成多个接口文档,但是在接口与文档归属上不一致:

      在Swashbuckle中,通过ApiExplorerSettingsAttribute特性的GroupName属性指定documentName来实现的,而NSwag虽然也是用ApiExplorerSettingsAttribute特性实现,但是此时的GroupName不在是documentName,而是ApiGroupNames属性指定的元素值了:

      比如下面三个接口:  

        /// <summary>
        /// 未使用ApiExplorerSettings特性,表名属于每一个swagger文档
        /// </summary>
        /// <returns>结果</returns>
        [HttpGet("All"), Authorize]
        public string All()
        {
            return "All";
        }
        /// <summary>
        /// 使用ApiExplorerSettings特性表名该接口属于swagger文档v1
        /// </summary>
        /// <returns>Get结果</returns>
        [HttpGet]
        [ApiExplorerSettings(GroupName = "demo1")]
        public string Get()
        {
            return "Get";
        }
        /// <summary>
        /// 使用ApiExplorerSettings特性表名该接口属于swagger文档v2
        /// </summary>
        /// <returns>Post结果</returns>
        [HttpPost]
        [ApiExplorerSettings(GroupName = "demo2")]
        public string Post()
        {
            return "Post";
        }

       定义两个文档:  

        services.AddOpenApiDocument(settings =>
        {
            settings.DocumentName = "v1";
            settings.Version = "v0.0.1";
            settings.Title = "测试接口项目";
            settings.Description = "接口文档说明";
            settings.ApiGroupNames = new string[] { "demo1" };
    
            settings.PostProcess = document =>
            {
                document.Info.Contact = new OpenApiContact()
                {
                    Name = "zhangsan",
                    Email = "xxx@qq.com",
                    Url = null
                };
            };
        });
        services.AddOpenApiDocument(settings =>
        {
            settings.DocumentName = "v2";
            settings.Version = "v0.0.2";
            settings.Title = "测试接口项目v0.0.2";
            settings.Description = "接口文档说明v0.0.2";
            settings.ApiGroupNames = new string[] { "demo2" };
    
            settings.PostProcess = document =>
            {
                document.Info.Contact = new OpenApiContact()
                {
                    Name = "lisi",
                    Email = "xxx@qq.com",
                    Url = null
                };
            };
        });

      这时不用像Swashbuckle还要在中间件中添加文档地址,NSwag中间件会自动根据路由模板和文档生成文档地址信息,所以直接运行就可以了:

      

       

      可以注意到,All既不属于v1文档也不属于v2文档,也就是说,如果设置了ApiGroupNames,那就回严格的按ApiGroupNames来比较,只有匹配的GroupName在ApiGroupNames属性中才算属于这个接口文档,这也是NSwag和Swashbuckle不同的一点。

      另外,同样的,NSwag也支持使用IActionModelConvention和IControllerModelConvention设置GroupName,具体可以参考上一篇博文

      UseControllerSummaryAsTagDescription   

      这个属性上面例子有介绍,因为NSwag的控制器标签默认从OpenApiTagAttribute中读取,而不是从注释文档读取,将此属性设置成 true就可以从注释文档读取了,但是读取的内容可被OpenApiTagAttribute特性覆盖。

      AddSecurity

      AddSecurity拓展方法用于添加认证,它是两个重载方法:  

        public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, OpenApiSecurityScheme swaggerSecurityScheme);
        public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, IEnumerable<string> globalScopeNames, OpenApiSecurityScheme swaggerSecurityScheme);

      虽然是重载,但是两个方法的作用差别还挺大,第一个(不带globalScopeNames参数)的方法的作用类似Swashbuckle的AddSecurityDefinition方法,只是声明的作用,而第二个(有globalScopeNames参数)的方法作用类似于Swashbuckle的AddSecurityRequirement方法,也就是说,这两个重载方法,一个仅仅是声明认证,另一个是除了声明认证,还会将认证全局的作用于每个接口,不过这两个方法的实现是使用DocumentProcessors(类似Swashbuckle的DocumentFilter)来实现的  

        /// <summary>Appends the OAuth2 security scheme and requirement to the document's security definitions.</summary>
        /// <remarks>Adds a <see cref="SecurityDefinitionAppender"/> document processor with the given arguments.</remarks>
        /// <param name="settings">The settings.</param>
        /// <param name="name">The name/key of the security scheme/definition.</param>
        /// <param name="swaggerSecurityScheme">The Swagger security scheme.</param>
        public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, OpenApiSecurityScheme swaggerSecurityScheme)
        {
            settings.DocumentProcessors.Add(new SecurityDefinitionAppender(name, swaggerSecurityScheme));
            return settings;
        }
    
        /// <summary>Appends the OAuth2 security scheme and requirement to the document's security definitions.</summary>
        /// <remarks>Adds a <see cref="SecurityDefinitionAppender"/> document processor with the given arguments.</remarks>
        /// <param name="settings">The settings.</param>
        /// <param name="name">The name/key of the security scheme/definition.</param>
        /// <param name="globalScopeNames">The global scope names to add to as security requirement with the scheme name in the document's 'security' property (can be an empty list).</param>
        /// <param name="swaggerSecurityScheme">The Swagger security scheme.</param>
        public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, IEnumerable<string> globalScopeNames, OpenApiSecurityScheme swaggerSecurityScheme)
        {
            settings.DocumentProcessors.Add(new SecurityDefinitionAppender(name, globalScopeNames, swaggerSecurityScheme));
            return settings;
        }

       而SecurityDefinitionAppender是一个实现了IDocumentProcessor接口的类,它实现的Porcess如下,其中_scopeNames就是上面方法传进来的globalScopeNames:

        /// <summary>Processes the specified Swagger document.</summary>
        /// <param name="context"></param>
        public void Process(DocumentProcessorContext context)
        {
            context.Document.SecurityDefinitions[_name] = _swaggerSecurityScheme;
    
            if (_scopeNames != null)
            {
                if (context.Document.Security == null)
                {
                    context.Document.Security = new Collection<OpenApiSecurityRequirement>();
                }
    
                context.Document.Security.Add(new OpenApiSecurityRequirement
                {
                    { _name, _scopeNames }
                });
            }
        }

       至于其他用法,可以参考上面的一般用法和上一篇中介绍的Swashbuckle的AddSecurityDefinition方法和AddSecurityRequirement方法的用法,很相似。

      DocumentProcessors

      DocumentProcessors类似于Swashbuckle的DocumentFilter方法,只不过DocumentFilter方法时实现IDocumentFilter接口,而DocumentProcessors一个IDocumentProcessor集合属性,是需要实现IDocumentProcessor接口然后添加到集合中去。需要注意的是,因为NSwag有缓存机制的存在DocumentProcessors可能只会执行一遍

      另外,你可能注意到,上面有介绍过一个PostProcess方法,其实个人觉得PostProcess和DocumentProcessors区别不大,但是DocumentProcessors是在PostProcess之前调用执行,源码中:  

        public async Task<OpenApiDocument> GenerateAsync(ApiDescriptionGroupCollection apiDescriptionGroups)
        {
            ...

         foreach (var processor in Settings.DocumentProcessors) { processor.Process(new DocumentProcessorContext(document, controllerTypes, usedControllerTypes, schemaResolver, Settings.SchemaGenerator, Settings)); } Settings.PostProcess?.Invoke(document); return document; }

       可能是作者觉得DocumentProcessors有点绕,所以提供了一个委托供我们简单处理吧,用法也可以参考上一篇中的Swashbuckle的DocumentFilter方法,比如全局的添加认证,全局的添加Server等等。

      OperationProcessors

       OperationProcessors类似Swashbuckle的OperationFilter方法,只不过OperationFilter实现的是IOperationFilter,而OperationProcessors是IOperationProcessor接口集合。需要注意的是,因为NSwag有缓存机制的存在OperationProcessors可能只会执行一遍

      同样的,可能作者为了方便我们使用,已经定义好了一个OperationProcessor类,我们可以将我们的逻辑当做参数去实例化OperationProcessor类,然后添加到OperationProcessors集合中即可,不过作者还提供了一个AddOperationFilter方法,可以往OperationProcessors即可开始位置添加过期操作:  

        /// <summary>Inserts a function based operation processor at the beginning of the pipeline to be used to filter operations.</summary>
        /// <param name="filter">The processor filter.</param>
        public void AddOperationFilter(Func<OperationProcessorContext, bool> filter)
        {
            OperationProcessors.Insert(0, new OperationProcessor(filter));
        }

       所以我们可以这么用:  

        settings.AddOperationFilter(context =>
        {
            //我们的逻辑
            return true;
        });

      另外,因为无论使用AddOperationFilter方法,还是直接往OperationProcessors集合中添加IOperationProcessor对象,都会对所有Action(或者说Operation)进行调用,NSwag还有一个SwaggerOperationProcessorAttribute特性,用于指定某些特定Action才会调用执行。当然,SwaggerOperationProcessorAttribute的实例化需要指定一个实现了IOperationProcessor接口的类型以及实例化它所需要的的参数。

      与Swashbuckle不同的是,IOperationProcessor的Process接口要求返回一个bool类型的值,表示接口是否要在swaggerUI页面展示,如果返回false,接口就不会在前端展示了,而且后续的IOperationProcessor对象也不再继续调用执行。  

        private bool RunOperationProcessors(OpenApiDocument document, Type controllerType, MethodInfo methodInfo, OpenApiOperationDescription operationDescription, List<OpenApiOperationDescription> allOperations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver)
        {
            var context = new OperationProcessorContext(document, operationDescription, controllerType,
                methodInfo, swaggerGenerator, Settings.SchemaGenerator, schemaResolver, Settings, allOperations);
    
            // 1. Run from settings
            foreach (var operationProcessor in Settings.OperationProcessors)
            {
                if (operationProcessor.Process(context)== false)
                {
                    return false;
                }
            }
    
            // 2. Run from class attributes
            var operationProcessorAttribute = methodInfo.DeclaringType.GetTypeInfo()
                .GetCustomAttributes()
            // 3. Run from method attributes
                .Concat(methodInfo.GetCustomAttributes())
                .Where(a => a.GetType().IsAssignableToTypeName("SwaggerOperationProcessorAttribute", TypeNameStyle.Name));
    
            foreach (dynamic attribute in operationProcessorAttribute)
            {
                var operationProcessor = ObjectExtensions.HasProperty(attribute, "Parameters") ?
                    (IOperationProcessor)Activator.CreateInstance(attribute.Type, attribute.Parameters) :
                    (IOperationProcessor)Activator.CreateInstance(attribute.Type);
    
                if (operationProcessor.Process(context) == false)
                {
                    return false;
                }
            }
    
            return true;
        }

       至于其它具体用法,具体用法可以参考上一篇介绍的Swashbuckle的OperationFilter方法,如给特定Operation添加认证,或者对响应接口包装等等。

      其它配置

      AspNetCoreOpenApiDocumentGeneratorSettings继承于OpenApiDocumentGeneratorSettings和JsonSchemaGeneratorSettings还有茫茫多的配置,感兴趣的自己看源码吧,毕竟它和Swashbuckle差不多,一般的需求都能满足了,实现满足不了,可以使用DocumentProcessors和OperationProcessors来实现,就跟Swashbuckle的DocumentFilter和OperationFilter一样。

      但是有些问题可能就不行了,比如虚拟路径问题,Swashbuckle采用在Server上加路径来实现,而因为NSwag没有像Swashbuckle的AddServer方法,想到可以使用上面的PostProcess方法或者使用DocumentProcessors来实现,但是现实是打脸,因为作者的处理方式是,执行PostProcess方法和DocumentProcessors之后,会把OpenAPIDocument上的Servers先清空,然后再加上当前SwaggerUI所在的域名地址,可能作者觉着这样能满足大部分人的需求吧。但是作者还是提供了其他的方式来操作,会在后面的中间件中介绍

      三、添加Swagger中间件(UseOpenApi、UseSwagger和UseSwaggerUi3、UseSwaggerUi)

      UseOpenApi、UseSwagger

      首先UseOpenApi、UseSwagger和Swashbuckle的UseSwagger的作用一样的,主要用于拦截swagger.json请求,从而可以获取返回所需的接口架构信息,不同点在于NSwag的UseOpenApi、UseSwagger具有缓存机制,也就是说,如果第一次获取到了接口文档,会已json格式将文档加入到本地缓存中,下次直接从缓存获取,因为缓存的存在,所以上面介绍的OperationProcessors和DocumentProcessors都不会再执行了。

      另外,UseSwagger是旧版本,已经不推荐使用了,推荐使用UseOpenApi:  

        app.UseOpenApi(settings =>
        {
            //中间件设置
        });

      OpenApiDocumentMiddlewareSettings

      UseOpenApi依赖OpenApiDocumentMiddlewareSettings对象完成配置过程,主要属性有:

      Path

      Path表示拦截请求的格式,也就是拦截swagger.json的路由格式,这个跟Swashbuckle一样,因为需要从路由知道是哪个文档,然后才能去找这个文档的所有接口解析返回,它的默认值是 /swagger/{documentName}/swagger.json。

      同样的,因为这个值关系比较重要,尽可能不要去修改吧。

      DocumentName

      从上面的Path参数的默认值中可以看到,其中有个{documentName}参数NSwag并没有要求Path中必须有{documentName}参数。

      如果没有这个参数,就必须指定这个属性DocumentName,只是也就是说NSwag只为一个接口文档服务。

      如果有这个参数,NSwag会遍历所有定义的接口文档,然后分别对Path属性替换掉其中中的{documentName}参数,然后分别拦截每个文档获取架构信息的swagger.json请求。

      PostProcess

      服务注入部分有一个PostProcess方法,功能其实类似于DocumentProcessors,就是对接口文档做一个调整,而现在这里又有一个PostProcess方法,它则是根据当前请求来调整接口文档用的。

      比如,上面有介绍,如果在服务注入部分使用PostProcess方法或者DocumentProcessors添加了Server,是没有效果的,这个是因为NSwag在获取到文档之后,有意的清理了文档的Servers属性,然后加上了当前请求的地址:  

        /// <summary>Generates the Swagger specification.</summary>
        /// <param name="context">The context.</param>
        /// <returns>The Swagger specification.</returns>
        protected virtual async Task<OpenApiDocument> GenerateDocumentAsync(HttpContext context)
        {
            var document = await _documentProvider.GenerateAsync(_documentName);
    
            document.Servers.Clear();
            document.Servers.Add(new OpenApiServer
            {
                Url = context.Request.GetServerUrl()
            });
    
            _settings.PostProcess?.Invoke(document, context.Request);
    
            return document;
        }

      注意到上面的源码,在清理之后,还调用了这个PostProcess委托,因此,我们可以将添加Server部分的代码写到这个PostProcess中:  

        app.UseOpenApi(settings =>
        {
            settings.PostProcess = (document, request) =>
            {
                //清理掉NSwag加上去的
                document.Servers.Clear();
                document.Servers.Add(new OpenApiServer() { Url = "http://localhost:90/NSwag", Description = "地址1" });
                document.Servers.Add(new OpenApiServer() { Url = "http://127.0.0.1:90/NSwag", Description = "地址2" });
                //192.168.28.213是我本地IP
                document.Servers.Add(new OpenApiServer() { Url = "http://192.168.28.213:90/NSwag", Description = "地址3" });
            };
        });

      看来,作者还是很友好的,做了点小动作还提供给我们一个修改的方法。

      CreateDocumentCacheKey

      上面有提到,NSwag的接口文旦有缓存机制,第一次获取之后就会以json格式被缓存,接下就会从缓存中读取,而CreateDocumentCacheKey就是缓存的键值工厂,用于生成缓存键值用的,如果不设置,那么缓存的键值就是string.Empty。

      那可能会问,如果不想用缓存呢,不妨设置CreateDocumentCacheKey成这样:  

        app.UseOpenApi(settings =>
        {
            settings.CreateDocumentCacheKey = request => DateTime.Now.ToString();
        });

      然后你就会发现,过了一段时间之后,你的程序挂了,OutOfMemory!

      所以,好好的用缓存的,从源码中目前没发现有什么办法可以取消缓存,况且使用缓存可以提高响应速度,为何不用?如果实在要屏蔽缓存,那就是改改源码再编译引用吧。

      ExceptionCacheTime

      既然是程序,那就有可能会抛出异常,获取接口文档架构也不例外,而ExceptionCacheTime表示在获取接口文档发生异常后的一段时间内,使用返回这个异常,ExceptionCacheTime默认是TimeSpan.FromSeconds(10)

      UseSwaggerUi3、UseSwaggerUi

      UseSwaggerUi3、UseSwaggerUi的作用和Swashbuckle的UseSwaggerUI作用是一样,主要用于拦截swagger/index.html页面请求,返回页面给前端。

      UseSwaggerUi返回的是基于Swagger2.0的页面,而UseSwaggerUi3返回的是基于Swagger3.0的页面,所以这里推荐使用UseSwaggerUi3  

        app.UseSwaggerUi3(settings =>
        {
            //中间件操作
        });

      SwaggerUi3Settings

      UseSwaggerUi3依赖SwaggerUi3Settings完成配置,SwaggerUi3Settings继承于SwaggerUiSettingsBase和SwaggerSettings,所以属性比较多,这里介绍常用的一些属性:

      EnableTryItOut

      这个属性很简单,就是设置允许你是否可以在SwaggerUI使用Try it out去调用接口

      DocumentTitle

      这是SwaggerUI页面的Title信息,也就是返回的html的head标签下的title标签值,默认是 Swagger UI

      CustomHeadContent

      自定义页面head标签内容,可以使用自定义的脚本和样式等等,作用于Swashbuckle中提到的HeadContent是一样的

      Path

      Path是SwaggerUI的index.html页面的地址,作用与Swashbuckle中提到的RoutePrefix是一样的

      CustomInlineStyles

      自定外部样式,不是链接,就是具体的样式!

      CustomInlineStyles

      自定义的外部样式文件的链接

      CustomJavaScriptPath

      自定义外部JavaScript脚本文件的连接

      DocumentPath

      接口文档获取架构swagger.json的Url模板,NSwag不需要想Swashbuckle调用SwaggerEndpoint添加文档就是因为它会自动根据这个将所有文档按照DocumentPath的格式进行设置,它的默认值是 /swagger/{documentName}/swagger.json。

      同样的,尽可能不要修改这个属性,如果修改了,切记要和上面介绍的OpenApiDocumentMiddlewareSettings的Path属性同步修改。

      SwaggerRoutes

      这是属性包含了接口文档列表,在Swashbuckle中是通过SwaggerEndpoint方法添加的,但是NSwag会自动生成根据DocumentPath属性自动生成。  

        app.UseSwaggerUi3(settings =>
        {
            settings.SwaggerRoutes.Add(new NSwag.AspNetCore.SwaggerUi3Route("demo", "/swagger/v1/swagger.json"));
        });

      需要注意的是,如果自己往SwaggerRoutes中添加接口文档对象,那么NSwag不会自动生成了,比如上面的例子,虽然定义了多个文档,但是我们手动往SwaggerRoutes添加了一个,那SwaggerUI中就只会显示我们自己手动添加的了。

      TransformToExternalPath

      TransformToExternalPath其实是一个路径转化,主要是转换swagger内部的连接,比如获取架构新的的请求 /swagger/v1/swagger.json和获取swaggerUI页面的连接 /swagger,这个很有用,比如上面提到的虚拟路径处理的一个完整的例子: 

      
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using NSwag;
    
    namespace NSwagDemo
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddOpenApiDocument(settings =>
                {
                    settings.DocumentName = "v1";
                    settings.Version = "v0.0.1";
                    settings.Title = "测试接口项目";
                    settings.Description = "接口文档说明";
                    settings.ApiGroupNames = new string[] { "demo1" };
    
                    settings.PostProcess = document =>
                    {
                        document.Info.Contact = new OpenApiContact()
                        {
                            Name = "zhangsan",
                            Email = "xxx@qq.com",
                            Url = null
                        };
                    };
    
                    settings.AddOperationFilter(context =>
                    {
                        //我们的逻辑
                        return true;
                    });
    
                    //可以设置从注释文件加载,但是加载的内容可被OpenApiTagAttribute特性覆盖
                    settings.UseControllerSummaryAsTagDescription = true;
    
                    //定义JwtBearer认证方式一
                    settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
                    {
                        Description = "这是方式一(直接在输入框中输入认证信息,不需要在开头添加Bearer)",
                        Name = "Authorization",//jwt默认的参数名称
                        In = OpenApiSecurityApiKeyLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                        Type = OpenApiSecuritySchemeType.Http,
                        Scheme = "bearer"
                    });
    
                    //定义JwtBearer认证方式二
                    settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
                    {
                        Description = "这是方式二(JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格))",
                        Name = "Authorization",//jwt默认的参数名称
                        In = OpenApiSecurityApiKeyLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                        Type = OpenApiSecuritySchemeType.ApiKey
                    });
                });
    
                services.AddAuthentication();
                services.AddControllers();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseRouting();
    
                //NSwag是虚拟路径
                var documentPath = "/swagger/{documentName}/swagger.json";
                app.UseOpenApi(settings =>
                {
                    settings.PostProcess = (document, request) =>
                    {
                        //清理掉NSwag加上去的
                        document.Servers.Clear();
                        document.Servers.Add(new OpenApiServer() { Url = "http://localhost:90/NSwag", Description = "地址1" });
                        document.Servers.Add(new OpenApiServer() { Url = "http://127.0.0.1:90/NSwag", Description = "地址2" });
                        //192.168.28.213是我本地IP
                        document.Servers.Add(new OpenApiServer() { Url = "http://192.168.28.213:90/NSwag", Description = "地址3" });
                    };
                    settings.Path = documentPath;
                });
                app.UseSwaggerUi3(settings =>
                {
                    //settings.SwaggerRoutes.Add(new NSwag.AspNetCore.SwaggerUi3Route("demo", "/swagger/v1/swagger.json"));
                    settings.TransformToExternalPath = (s, r) =>
                     {
    
                         if (s.EndsWith("swagger.json"))
                         {
                             return $"/NSwag{s}";
                         }
                         return s;
                     };
                });
    
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }
    虚拟路径例子

      比如这里我们的虚拟路径是NSwag,使用IIS部署:

      

        项目运行后

      

       

      四、总结

       后面还有东西就不写了,还是那三个注意点:

      主要就是记住三点:

      1、服务注入使用AddOpenApiDocument方法(尽量不要用AddSwaggerDocument),主要就是生成接口相关信息,如认证,接口注释等等,还有几种过滤器帮助我们实现自己的需求

      2、中间件注入有两个:UseOpenApi(尽量不要使用UseSwagger,后续版本将会被移除)和UseSwaggerUi3(尽量不要使用UseSwaggerUi,后续版本将会被移除):

         UseOpenApi负责返回接口架构信息,返回的是json格式的数据

         UseSwaggerUi3负责返回的是页面信息,返回的是html内容

      3、如果涉及到接口生成的,尽可能在AddOpenApiDocument中实现,如果涉及到UI页面的,尽可能在UseSwaggerUi3中实现

  • 相关阅读:
    在C#程序中模拟发送键盘按键消息
    C# UDPCLIENT多线程实例
    【转】WINFORM下FTP客户端的实现
    抓取屏幕或窗体并保存成图片
    c# 判断窗体已打开则显示为焦点
    【转】C#中dataGridView用法实例分析
    【转】VC6控件小记
    .Net部署二三事之一——如何为VS安装文件MSI制作更新补丁MSP
    [转]C#串口serialPort操作
    VS向IIS发布C#或VB应用程序
  • 原文地址:https://www.cnblogs.com/shanfeng1000/p/13476834.html
Copyright © 2020-2023  润新知