Spring3 集成 OpenAPI 生成接口文档
1. 依赖
Spring 版本:3.0.5
Java 版本:jdk21
OpenAPI 依赖:
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
官网:OpenAPI 规范 | OpenAPI 官方文档中文版 (xiniushu.com)
不要包含其他的接口文档的框架,大概率是冲突的
目前我还没有找到 Spring3 的其他接口文档方案,swagger2 和 swagger3 都因为不兼容搞不了;
2. 配置文件
默认整个类路径全扫描
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI springOpenAPI() {
return new OpenAPI().info(new Info()
.title("SpringDoc API Test")
.description("SpringDoc Simple Application Test")
.version("1.0"));
}
}
3. 关键用法
3.1 Tag 注解
对接口文档进行分组,一个 controller 类为单位
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1")
@Tag(name = "xxx")
public class xxxController {
}
3.2 Operation 注解
对接口进行描述,一个目标方法为单位
@PostMapping("/create")
@Operation(summary = "创建一个 XXX")
public SystemJsonResponse<CustomClass> createXXX() {
}
3.3 Parameter 与 Header 注解
Parameter 描述一个接口的参数与路径参数,Header 描述一个接口的请求头,他们作用于目标方法的形参
@PostMapping("/get/{id}")
@Operation(summary = "获得一个 XXX")
public SystemJsonResponse<CustomClass> getXXX(@RequestHeader("token") @Header(description = "token") String token,
@PathVariable("id") @Parameter(description = "id") Long id,
@RequestParam(name = "state") @Parameter(description = "state") Integer state) {
}
3.4 Schema 注解
Schema 描述一个数据模型,也可以描述一个属性,作用于类或者字段
name 可指定属性的名称,description 是属性的描述
name 作为数据模型(类)的名称,description 是数据模型(类)的描述
属性得 public 或者有 public 的 Getter 和 Setter,否则会被标注只读或只写,如果都没有则直接忽略,如果一个数据模型属性都没有,忽略这个数据模型,复杂点的情况还会出现莫名其妙的现象
总而言之规范地写就没问题,框架也没义务和精力去控制各种奇奇怪怪地情况,我们也没必要总结
@TableName(value ="user")
@Schema(description = "用户")
@Data
public class User implements Serializable {
@Schema(description = "ID")
private Long id;
@Schema(description = "昵称")
private String nickname;
private static final long serialVersionUID = 1L;
}
3.5 SchemaProperty 注解
SchemaProperty 作用于一个字段,无法设置属性名,name 作为属性的描述
这个注解不能作用于自定义的数据模型,自定义对象会被认定为 string,因为这个注解就是作用于普通的字段,并不支持递归扫描,这个时候应该用 Schema
- 建议都用 Schema,SchemaProperty 功能还是太弱了
@SchemaProperty(name = "ID")
private Long id;
3.6 Hidden
作用于字段,表示忽略字段
@Hidden
private String nickname;
3.7 nullable = true
注解参数设置这个代表参数可以为 null
@Schema(description = "昵称", nullable = true)
private String nickname;
3.8 响应的数据模型
OpenAPI 提供了 @ApiRespnse 注解,但是很不方便,不如直接去 Postman 或者 Apifox 上进行操作,理想状态就是直接扫描出「请求body对象」和「响应body对象」 Schema 注解描述的数据模型
请求的话,作为参数是类型是确定的,OpenAPI 框架自然是扫描的到的,但是响应的数据模型,一般是统一响应格式的:
@Schema(name = "统一响应")
@NoArgsConstructor
@Getter
public class SystemJsonResponse implements Serializable {
private static final long serialVersionUID = 1L;
@JsonInclude
@Schema(description = "状态码")
private int code;
@JsonInclude
@Schema(description = "描述语")
private String message;
@JsonInclude
@Schema(nullable = true)
private Object data;
}
返回的时候,构造 json 是没问题的,但是框架扫描不出来这个 data 数据模型,因为框架只知道 data 是 Object 类型
你可能想问:“框架拿到 data 用 getClass 不就知道了吗?”
是的,但是接口文档生成是在这个接口被调用之前,所以并没有一个 data 数据提供给框架
你很容易想到用范型来定义统一响应:
@Schema(name = "统一响应")
@NoArgsConstructor
@Getter
public class SystemJsonResponse<T> implements Serializable {
private static final long serialVersionUID = 1L;
@JsonInclude
@Schema(description = "状态码")
private int code;
@JsonInclude
@Schema(description = "描述语")
private String message;
@JsonInclude
@Schema(nullable = true)
private T data;
}
但是实践这个方式,你会发现生成的接口文档,data 仍然是 object
其实原因很简单,用 @Schema 将 SystemJsonResponse 描述作一个固定的数据模型,那每个以此对象为返回值的目标方法,接口文档都直接使用这个数据模型
而泛型在定义的时候,本质还是 Object
解决方案也很简单,去掉 @Schema 即可,这样框架扫描目标方法返回的 SystemJsonResponse<CustomClass>
的时候,都会创建一个新的数据模型,这个时候就可以通过泛型类型确定准确的响应数据模型了,若存在一个一模一样的才会复用
注意:
- 不支持通配符:
SystemJsonResponse<?>
SystemJsonResponse<? extends CustomClass>
,则仅仅扫描 CustomClass
3.9 补充
GetMapping 的接口,RequestBody 会被扫描成 queryString,而不是 json
4. 访问
http://[host]:[post]/swagger-ui/index.html
http://[host]:[post]/v3/api-docs
更多细节需要去查去探索这里不一一罗列