Springdoc 生成 API 文档 — 迁移自 Springfox
傻傻分不清的近义词辨析
提到 Spring 自动生成 API 文档,多数人脑中就浮现出几个单词:Swagger
, OpenAPI
, Springfox
... 所以首先有必要搞清楚他们的关系。
- OpenAPI 是一个联合组织,负责制定标准的、开放的 API 描述规范。使用 openAPI 规范的 API 描述文件通常可以兼容各种相关软件或系统。
- Swagger 一开始是一个组织,制定了最流行的 API 描述格式,并将其捐献给 OpenAPI 组织衍化成最初的 OpenAPI 标准。同时提供一些列的周边工具,包括著名的
swagger-ui
。后来被 SmartBear 公司收购,又提供了Swagger Hub
等商业产品。 - Springfox 是一个适用于 Spring 的库,能够自动化生成 API 描述文件,支持 Swagger2, Swagger3 以及 OpenAPI3 三种格式。
- Springdoc 是另一个适用于 Spring 的库,能生成 OpenAPI3 格式的 API 描述文件。
总结一下就是 OpenAPI
是一个联盟与规范,Springfox
与 Springdoc
都是这个规范的实现。Swagger
是一个公司,贡献了许多好用的工具并且参与制定了 OpenAPI 规范。
Why Springdoc
显然的,Springfox 近期不是很活跃,光是 Springfox3.x 发布,就距离上一次过去了两年之久。不过这里要为它正名一下,如今 Springfox 通用支持 Swagger3 与 OpenAPI3 规范。 而相比之下,Springdoc 项目非常活跃,文档也更加全面,尽管只支持 OpenAPI3,但鉴于这是最新的标准化规范,所以已经足够了。
此外 Springdoc 还额外支持 Spring WebFlux。
从 Springfox 迁移
迁移过程没有太多技术含量,官方文档 已经写的挺详细了,总共分为三部。
- 添加 Springdoc 依赖:
implementation("org.springdoc:springdoc-openapi-ui:1.5.9")
。 - 删除 Springfox 的相关依赖。
- 替换注解与部分配置。文档中已经列出了非常详细的注解对应方案。
唯一要注意的就是,是的,你没看错,原 @ApiModel
, @ApiModelProperty
都替换为 @Schema
。里面部分参数名有所不同,建议 @ApiModelProperty(value="xxx")
替换为 @Schema(description="xxx")
。
⚠ 对于 Kotlin 有个大坑! 记得额外添加 implementation("org.springdoc:springdoc-openapi-kotlin:1.5.9")
这个依赖,其实文档中也写了的只是不在迁移指南里。否则进入 Swagger-ui 中会发现除了一个外,其他属性都不生效了。
添加默认响应
在 Springfox 中,我们可以用类似下面的代码添加全局的响应结构:
@Bean
fun createApi(): Docket {
val responses = listOf<Response>(
ResponseBuilder().code("401").description("Unauthorized (未登录)").build(),
ResponseBuilder().code("403").description("Forbidden (登录但无权操作)").build()
)
return Docket(DocumentationType.OAS_30)
.useDefaultResponseMessages(false)
.globalResponses(HttpMethod.GET, responses)
.globalResponses(HttpMethod.POST, responses)
.globalResponses(HttpMethod.DELETE, responses)
// 其他操作
.build()
}
后起之秀 Springdoc 自然也可以实现,而且更加灵活了。正因为更加灵活了,所以写起来稍微麻烦了一点:
@Bean
fun customizeOperation(): OpenApiCustomiser {
return OpenApiCustomiser { openApi ->
// 取得默认响应码的 schema
val schema = openApi.components.schemas[HttpErrorResp::class.java.simpleName]
openApi.paths.values.forEach { pathItem ->
pathItem.readOperations().forEach { operation ->
// 添加默认响应码
if ("401" !in operation.responses)
createApiResp("Unauthorized (未登录)", schema)
.also { operation.responses.addApiResponse("401", it) }
if ("403" !in operation.responses)
createApiResp("Forbidden (登录但无权操作)", schema)
.also { operation.responses.addApiResponse("403", it) }
}
}
}
}
private fun createApiResp(description: String, schema: Schema<Any>?): ApiResponse {
val mediaType = io.swagger.v3.oas.models.media.MediaType()
.schema(schema)
return ApiResponse().description(description).content(
Content().addMediaType(MediaType.APPLICATION_JSON_VALUE, mediaType)
)
}
对于 Springdoc 我们需要构造一个 OpenApiCustomiser
对象,用来对最终生成的 Api 进行修饰。首先我们读取已被识别的 schema
取得所需要的那个响应体。当然,根据工程实际情况,你也可以实例化一个 Schema
对象而不是获取一个现成的。
然后遍历所有的 paths
,对于每一项,遍历它所有的 operation
,然后依次给它们添加默认响应就可以了。这里进行了额外判断,如果响应码不存在那么再添加,否则会覆盖掉 API 定义的响应。这也看出 Springdoc 的灵活之处,此处可以添加任意逻辑,对 API 进行任意修改。比 Springfox 那种固定 API 的默认强大许多。
配置项
最后,Springdoc 非常人性化地提供了众多配置项,可以直接写在 properties
或 yaml
文件中。众所周知 Swagger2 默认的 url 是 .../swagger-ui.html
,然鹅 Swagger3 变成了 .../swagger-ui/
。Springdoc 默认使用 Swagger2 的格式,让部分小伙伴很是难受(没错,就是我了。那么只需要...
springdoc:
swagger-ui:
path: /swagger-ui
搞定! 更多配置项请移步 →官方文档 白白咯~