傻傻分不清的近义词辨析
提到 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
复制代码
搞定! 更多配置项请移步 →官方文档 白白咯~