基于.NetCore开发博客项目 StarBlog – (26) 集成Swagger接口文档

前言

这是StarBlog系列在2023年的第一篇更新😃~

在之前的文章里,我们已经完成了部分接口的开发,接下来需要使用 curl、Postman 这类工具对这些接口进行测试,但接口一多,每次测试都要一个个填入地址和对应参数会比较麻烦…

我们需要一种直观的方式来汇总项目里的所有接口,并且如果能直接在里面调试接口,那就更好了。

Swagger:诶嘿,说的不就是我吗?😎

Swagger介绍

来一段官网的介绍

Simplify API development for users, teams, and enterprises with the Swagger open source and professional toolset.

翻译:Swagger 是开源和专业的工具集,可以简化用户、团队和企业的 API 开发。

一般来说,swagger用起来有两部分,一个是 OpenAPI 一个是 SwaggerUI

在Swagger官网上,OpenAPI 介绍得天花乱坠🙂

The OpenAPI Specification, formerly known as the Swagger Specification, is the world’s standard for defining RESTful interfaces. The OAS enables developers to design a technology-agnostic API interface that forms the basis of their API development and consumption.

翻译:OpenAPI 规范,以前称为 Swagger 规范,是定义 RESTful 接口的世界标准。 OAS 使开发人员能够设计一个与技术无关的 API 接口,该接口构成了他们 API 开发和使用的基础。

简单说 OpenAPI 是个标准,需要每种语言和框架自行实现一个工具,用来把项目里的接口都整合起来,生成 swagger.json 文件

然后 SwaggerUI 就是个网页,读取这个 swagger.json 就可以把所有接口以及参数显示出来,还可以很方便调试,效果如图。

基于.NetCore开发博客项目 StarBlog - (26) 集成Swagger接口文档

Swashbuckle.AspNetCore

前面说到每种框架都要自己实现一个工具来生成 swagger.json ,这个 Swashbuckle.AspNetCore 就是 .NetCore 平台的实现,用就完事了。

项目主页: https://github.com/domaindrivendev/Swashbuckle.AspNetCore

Tips:如果是创建 WebApi 项目,代码模板里面默认就有 Swagger 了,不用手动添加。

StarBlog项目一开始是使用MVC模板,所以没有自带Swagger,需要手动添加。

直接使用nuget添加 Swashbuckle.AspNetCore 这个包就完事了。

这个包功能很多,内置了 SwaggerUI 这个官方界面,还有一个 ReDoc 的纯静态接口文档网页(这个 ReDoc 只能看接口不能调试)。

初步使用

为了保证 Program.cs 代码整洁,我们在 StarBlog.Web/Extensions 里面创建 ConfigureSwagger

public static class ConfigureSwagger {   public static void AddSwagger(this IServiceCollection services) {     services.AddSwaggerGen(options => {       options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "APIs"});        // 在接口文档上显示 XML 注释       var filePath = Path.Combine(System.AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml");       options.IncludeXmlComments(filePath, true);     });   }      public static void UseSwaggerPkg(this IApplicationBuilder app) {     app.UseSwagger();     app.UseSwaggerUI(options => {       options.RoutePrefix = "api-docs/swagger";       options.SwaggerEndpoint("/swagger/v1/swagger.json", "APIs");     });     app.UseReDoc(options => {       options.RoutePrefix = "api-docs/redoc";       options.SpecUrl = "/swagger/v1/swagger.json";     });   } } 

上面代码可以看到有三步

  • AddSwaggerGen - 对应前文说的生成 swagger.json
  • UseSwagger - 让浏览器可以访问到 /swagger/v1/swagger.json 这类路径
  • UseSwaggerUI - 提供 SwaggerUI 的网页访问

然后回到 Program.cs 里面,分别注册服务和添加中间件就好了。

// 注册服务 builder.Services.AddSwagger(); // 添加中间件 app.UseSwaggerPkg(); 

现在启动项目,访问 http://[本地地址]/api-docs/swagger 就能看到接口文档了

效果大概这样

基于.NetCore开发博客项目 StarBlog - (26) 集成Swagger接口文档

扩展:关于XML注释

C# 的代码注释可以导出XML,然后显示在 swagger 文档上

注意需要手动在 .csproj 项目配置里面开启,才会输出XML文档

<!--  输出XML  --> <PropertyGroup>   <GenerateDocumentationFile>true</GenerateDocumentationFile>   <NoWarn>$(NoWarn);1591</NoWarn> </PropertyGroup> 

但是开启XML之后,IDE很蠢的要求我们所有public成员都写上注释,很烦,加上 <NoWarn>$(NoWarn);1591</NoWarn> 这行就可以关掉这个警告。

在 Swagger 里加载XML文档,既可以用本文前面写的方式

var filePath = Path.Combine(System.AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml"); options.IncludeXmlComments(filePath, true); 

还可以用第二种,加载目录里的全部XML

var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml"); foreach (var file in xmlFiles) {   options.IncludeXmlComments(file, true); } 

具体用哪种,都行吧,看心情~

扩展:关于 AddEndpointsApiExplorer

AddSwagger 扩展方法这里可能有同学会有疑问

为啥创建 .Net6 项目后默认是这两行代码

builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); 

而我这里只有一行代码

services.AddSwaggerGen(); 

先说结论:AddEndpointsApiExplorer 是为了支持 Minimal Api 的。

因为 StarBlog 项目使用的是MVC模板,在 Program.cs 的最开始可以看到这行代码,添加控制器和视图

builder.Services.AddControllersWithViews(); 

翻一下这个框架的源码,可以看到这个方法的套娃是这样的

AddControllersWithViews() -> AddControllersWithViewsCore() -> AddControllersCore() 

而在 AddControllersCore 里面,又调用了 AddApiExplorer

private static IMvcCoreBuilder AddControllersCore(IServiceCollection services) {   // This method excludes all of the view-related services by default.   var builder = services     .AddMvcCore()     .AddApiExplorer()     .AddAuthorization()     .AddCors()     .AddDataAnnotations()     .AddFormatterMappings();    if (MetadataUpdater.IsSupported) {     services.TryAddEnumerable(       ServiceDescriptor.Singleton<IActionDescriptorChangeProvider, HotReloadService>());   }    return builder; } 

就是说正常的项目已经有 ApiExplorer 这个东西了,但是 Minimal Api 项目没有,所以本项目不需要 builder.Services.AddEndpointsApiExplorer(); 这行代码。

详情可以阅读参考资料的第一个链接。

接口分组

接口文档有了,但项目里接口太多了,几十个接口全挤在一个页面上,找都找得眼花了😑

这时候可以给接口分个组

先来给 StarBlog 项目里面的接口分个类,根据不同用途,大致分成这五类:

  • admin - 管理员相关接口
  • common - 通用公共接口
  • auth - 授权接口
  • blog - 博客管理接口
  • test - 测试接口

还是在上面那个 ConfigureSwagger.cs 文件

修改 AddSwagger 方法,把这几个分组添加进去

services.AddSwaggerGen(options => {   options.SwaggerDoc("admin", new OpenApiInfo {     Version = "v1",     Title = "Admin APIs",     Description = "管理员相关接口"   });   options.SwaggerDoc("common", new OpenApiInfo {     Version = "v1",     Title = "Common APIs",     Description = "通用公共接口"   });   options.SwaggerDoc("auth", new OpenApiInfo {     Version = "v1",     Title = "Auth APIs",     Description = "授权接口"   });   options.SwaggerDoc("blog", new OpenApiInfo {     Version = "v1",     Title = "Blog APIs",     Description = "博客管理接口"   });   options.SwaggerDoc("test", new OpenApiInfo {     Version = "v1",     Title = "Test APIs",     Description = "测试接口"   }); }); 

这样就会生成五个 swagger.json 文件,路径分别是

  • /swagger/admin/swagger.json
  • /swagger/common/swagger.json
  • /swagger/auth/swagger.json
  • /swagger/blog/swagger.json
  • /swagger/test/swagger.json

所以下面的 UseSwaggerPkg 方法也要对应修改

public static void UseSwaggerPkg(this IApplicationBuilder app) {   app.UseSwagger();   app.UseSwaggerUI(options => {     options.RoutePrefix = "api-docs/swagger";     options.SwaggerEndpoint("/swagger/admin/swagger.json", "Admin");     options.SwaggerEndpoint("/swagger/blog/swagger.json", "Blog");     options.SwaggerEndpoint("/swagger/auth/swagger.json", "Auth");     options.SwaggerEndpoint("/swagger/common/swagger.json", "Common");     options.SwaggerEndpoint("/swagger/test/swagger.json", "Test");   }); } 

接下来,要让 Swagger 知道每个接口都是属于哪个分组的。

具体方法是在 Controller 上添加 ApiExplorerSettings 特性。

比如 BlogController 是属于 blog 分组,在 class 定义前面添加一行代码

[ApiExplorerSettings(GroupName = "blog")] public class BlogController : ControllerBase {   // ... } 

其他的 Controller 也是类似的操作,具体分组跟 StarBlog.Web/Apis 下的目录结构一样,这里就不赘述了。

实现效果

做完之后,打开 swagger 接口文档页面

可以看到右上角可以选择接口分组了

基于.NetCore开发博客项目 StarBlog - (26) 集成Swagger接口文档

搞定。

优化分组

前文对于 Swagger 分组的实现其实是一种硬编码,不同分组的 Controller 上面需要加上 [ApiExplorerSettings(GroupName = "blog")] 特性,分组名全靠复制粘贴,在项目比较小的情况下还好,如果分组多起来了,有几百个接口的时候,估计人就麻了吧😂

Q:“你刚才干嘛不早说😑”

A:“循序渐进嘛😛”

A:“StarBlog项目也是最近才换到新版分组的😎”

StarBlog.Web/Models 里添加个新的类 SwaggerGroup

public class SwaggerGroup {     /// <summary>     /// 组名称(同时用于做URL前缀)     /// </summary>     public string Name { get; set; }      public string? Title { get; set; }     public string? Description { get; set; }      public SwaggerGroup(string name, string? title = null, string? description = null) {         Name = name;         Title = title;         Description = description;     }      /// <summary>     /// 生成 <see cref="Microsoft.OpenApi.Models.OpenApiInfo"/>     /// </summary>     public OpenApiInfo ToOpenApiInfo(string version = "1.0") {         var item = new OpenApiInfo();         Title ??= Name;         Description ??= Name;         return new OpenApiInfo { Title = Title, Description = Description, Version = version };     } } 

然后改造一下 StarBlog.Web/Extensions/ConfigureSwagger.cs

在这个文件里面添加个新的类,这样就不会硬编码了😃

public static class ApiGroups {   public const string Admin = "admin";   public const string Auth = "auth";   public const string Common = "common";   public const string Blog = "blog";   public const string Test = "test"; } 

ConfigureSwagger 里添加一些代码,创建 SwaggerGroup 列表

public static class ConfigureSwagger {   public static readonly List<SwaggerGroup> Groups = new() {     new SwaggerGroup(ApiGroups.Admin, "Admin APIs", "管理员相关接口"),     new SwaggerGroup(ApiGroups.Auth, "Auth APIs", "授权接口"),     new SwaggerGroup(ApiGroups.Common, "Common APIs", "通用公共接口"),     new SwaggerGroup(ApiGroups.Blog, "Blog APIs", "博客管理接口"),     new SwaggerGroup(ApiGroups.Test, "Test APIs", "测试接口")   }; } 

然后把后面的 AddSwagger 方法改成这样,那一坨东西,现在一行代码就代替了😀

public static void AddSwagger(this IServiceCollection services) {   services.AddSwaggerGen(options => {     Groups.ForEach(group => options.SwaggerDoc(group.Name, group.ToOpenApiInfo()));      // XML注释     var filePath = Path.Combine(AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml");     options.IncludeXmlComments(filePath, true);   }); } 

接着是 UseSwaggerPkg 方法,简单😎

public static void UseSwaggerPkg(this IApplicationBuilder app) {   app.UseSwagger();   app.UseSwaggerUI(opt => {     opt.RoutePrefix = "api-docs/swagger";     // 分组     Groups.ForEach(group => opt.SwaggerEndpoint($"/swagger/{group.Name}/swagger.json", group.Name));   }); } 

Controller里面也对应修改成这样

[ApiExplorerSettings(GroupName = ApiGroups.Blog)] public class BlogController : ControllerBase { } 

完美🆒🥳

小结

“Swagger之大,一锅炖不下”

关于Swagger还有其他的用法,但需要一些前置知识,因此本文不会把StarBlog项目中关于Swagger的部分全部介绍完

等把相关的前置知识写完,再来完善对应的用法~

这也跟StarBlog的开发过程是吻合的😀

参考资料

系列文章

发表评论

评论已关闭。

相关文章