Oh my God, Swagger API文档竟然可以这样写?

最好的总会在不经意间出现。

作为后端程序员,免不了与前端同事对接API, 一个书写良好的API设计文档可有效提高与前端对接的效率。

为避免联调时来回撕逼,今天我们聊一聊正确使用Swaager的姿势。

基础Swagger用法

ConfigureServices配置Swagger文档,在Configure启用中间件

 // Install-Package Swashbuckle.AspNetCore 略 
 services.AddSwaggerGen(
       options =>
       {
           options.SwaggerDoc("v1", new OpenApiInfo { Title = "EAP API", Version = "v1" });
       }
  );     
---

app.UseSwagger();
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "EAP API");
});

应用会在/Swagger页面加载最基础的API文档。

以一个最简单的Post请求为例,细数这最基础SwaggerUI的弊病

[HttpPost]
public async Task<bool> AddHotmapAsync([FromBody] CreateHotmapInput createHotmapInput)
{
     var model = ObjectMapper.Map<CreateHotmapInput, Hotmap>(createHotmapInput);
     model.ProfileId = CurrentUser.TenantId;
     return await _hotmaps.InsertAsync(model)!= null;
}

产生如图示SwaggerUI:

  1. Post请求的Payload字段值相对复杂,竟不给前端传值example?
  2. 没有指示前端请求的Content-Type,前端会不会给你另外一个surprise?
  3. 服务器没有指示响应的Content-Type,?
  4. 服务器没有指示响应的预期状态码,前端会不会抓狂?

下文就来根治这些顽疾, 书写一个自述性、优雅的API文档。

Swagger最佳实践

三下五除二,给出示例:

        /// <summary>
        /// 添加热力图
        /// </summary>
        /// <remarks>
        /// Sample request:
        /// ```
        ///  POST /hotmap
        ///  {
        ///      "displayName": "演示名称1",
        ///      "matchRule": 0,
        ///      "matchCondition": "//www.cnblogs.com/JulianHuang/",
        ///      "targetUrl": "//www.cnblogs.com/JulianHuang/",
        ///      "versions": [
        ///      {
        ///         "versionName": "ver2020",
        ///         "startDate": "2020-12-13T10:03:09",
        ///         "endDate": "2020-12-13T10:03:09",
        ///          "offlinePageUrl": "3fa85f64-5717-4562-b3fc-2c963f66afa6",  //  没有绑定图片和离线网页的对应属性传 null
        ///          "pictureUrl": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        ///          "createDate": "2020-12-13T10:03:09"
        ///      }
        ///    ]
        ///  }
        ///```
        /// </remarks>
        /// <param name="createHotmapInput"></param>
        /// <returns></returns>
        [Consumes("application/json")]
        [Produces("text/plan")]
        [ProducesResponseType(typeof(Boolean), 200)]
        [HttpPost]
        public async Task<bool> AddHotmapAsync([FromBody] CreateHotmapInput createHotmapInput)
        {
            var model = ObjectMapper.Map<CreateHotmapInput, Hotmap>(createHotmapInput);
            model.ProfileId = CurrentUser.TenantId;
            return await _hotmaps.InsertAsync(model)!=null;
        }
  1. 通过给API添加XML注释

注意如果注释内容包含代码,可以使用Markdown的代码语法“`,在注释里面优雅显示代码.

  1. 通过Consumes,Produces特性指示action接收和返回的数据类型,也就是媒体类型。

Consumes、Produces是指示请求/响应支持的content types的过滤器,位于Microsoft.AspNetCore.Mvc命名空间下。

  1. 通过ProducesResponseType特性指示服务器响应的预期内容和状态码

API文档结果:

这样的SwaggerUI才正确表达了后端程序员的内心输出。


在Swagger文档上显示注释

  1. 生成XML注释文档
    在项目上[右键]-[属性]-[生成标签页]-[勾选XML文档文件];
    或者直接在项目csproj文件–[PropertyGroup]添加<GenerateDocumentationFile>true</GenerateDocumentationFile>
  2. AddSwaggerGen方法添加下行,启用注释文件
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{this.GetType().Assembly.GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
opt.IncludeXmlComments(xmlPath);

这里啰嗦一下,如果是Abp Vnext解决方案(API是定义在HttpApi项目/Application项目),故我们要为Abp Vnext解决方案加载带xml注释的Swagger Json,需要指示xmlFile为HttpApi.xml或者applicaiton.xml

以上就是小码甲总结的书写Swagger文档的优雅姿势:

  • 编写API 传值example
  • 约束请求/响应 支持的媒体类型
  • 指示API的预期输出内容、预期状态码

API自述,约束输入输出,前端同事再也不会追着你撕逼了!