springdoc-openapi 简介
springdoc-openapijava库有助于使用 spring boot 项目自动生成 API 文档。 springdoc-openapi通过在运行时检查应用程序以根据 spring 配置、类结构和各种注解推断 API 语义来工作。
自动生成 JSON/YAML 和 HTML 格式 API 的文档。可以使用 swagger-api 注解通过注解来完成此文档。
该库支持:
- OpenAPI3
- SpringBoot (v1, v2 and v3)
- JSR-303, specifically for @NotNull, @Min, @Max, and @Size.
- Swagger-ui
- OAuth 2
- GraalVM 原生镜像
为什么使用 springdoc-openapi🤔
由于之前项目一直使用的是springfox3.0来集成swagger管理API接口文档,但目前springfox已经停止维护了。最近在升级底层框架时看到spring官方推荐使用springdoc,在自己一步一步查找相关资料时,发现国内对于这块的参考资料较少,也不全面。故写此篇文章来帮助大家快速集成。
Knife4j简介
Knife4j是一个集Swagger2和OpenAPI3为一体的增强解决方案。
开始集成
Maven引入
首先在maven里引入springdoc-openapi:
2024-01-23 更新:对于SpringBoot 2.x,引入1.7.0这个版本(后续好像不会对SpringBoot 2.x更新版本);对于SpringBoot 3.x,引入2以上版本,且包名不同
SpringBoot 2.x(参考文档):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
SpringBoot 3.x(参考文档):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
注解的区别
这里需要注意,我们要用swagger3注解替换swagger2注解(它已经包含在springdoc-openapi-ui依赖项中)。swagger 3 注解的包是io.swagger.v3.oas.annotations:
@Api -> @Tag
@ApiIgnore -> @Parameter(hidden = true) 或 @Operation(hidden = true) 或 @Hidden
@ApiImplicitParam -> @Parameter
@ApiImplicitParams -> @Parameters
@ApiModel -> @Schema
@ApiModelProperty(hidden = true) -> @Schema(accessMode = READ_ONLY)
@ApiModelProperty -> @Schema
@ApiOperation(value = "foo", notes = "bar") -> @Operation(summary = "foo", description = "bar")
@ApiParam -> @Parameter
@ApiResponse(code = 404, message = "foo") -> @ApiResponse(responseCode = "404", description = "foo")
以下举几个简单的🌰:
Controller:
@Tag(name = "用户接口")
@RestController
@RequestMapping("sys/user")
public class SysUserController {
@Resource
private ISysUserService sysUserService;
@Operation(summary = "分页查询")
@GetMapping("page")
public AjaxResult queryPage(@ParameterObject SysUserPageDTO dto) {
PageInfo page = sysUserService.queryPage(dto);
return AjaxResult.success(page);
}
@Operation(summary = "详情")
@GetMapping("{id}")
public AjaxResult queryInfo(@PathVariable Long id) {
SysUserDTO dto = sysUserService.queryById(id);
return AjaxResult.success(dto);
}
@Operation(summary = "新增")
@PostMapping
public AjaxResult save(@RequestBody SysUserDTO dto) {
Long id = sysUserService.saveInfo(dto);
return AjaxResult.success(id);
}
}
POST请求的DTO:
@Schema(description = "用户 数据传输对象")
@Data
@Accessors(chain = true)
public class SysUserDTO implements Serializable {
@Schema(description = "ID")
private Long id;
@Schema(description = "用户名")
private String userName;
@Schema(description = "真实姓名")
private String realName;
@Schema(description = "密码")
private String password;
@Schema(description = "性别(0男,1女)")
private Integer sex;
@Schema(description = "电话号码")
private String phone;
@Schema(description = "状态(0停用,1正常)")
private Integer status;
}
❗ 如果你使用一个对象来封装你GET请求的参数,会将你的参数封装为一个json,这不是我们想要的:
要解决这样的问题,有两种解决方法(感谢@nano发现及解决的问题):
方法一:
在官方文档里有这么一句话:“如果您使用一个对象来捕获多个请求查询参数,请注解该方法参数 @ParameterObject”。所以你应该在你get请求封装的对象前加注解 @ParameterObject,同时应该在对象里的参数注解使用 @Parameter,而不是 @Scheme。
GET接口:
@Operation(summary = "分页查询")
@GetMapping("page")
public AjaxResult queryPage(@ParameterObject SysUserPageDTO dto) {
PageInfo page = sysUserService.queryPage(dto);
return AjaxResult.success(page);
}
GET请求封装的DTO:
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class SysUserPageDTO extends BasePageDTO {
@Parameter(description = "用户名")
private String username;
}
方法二:
添加配置(添加此配置后,就不需要添加额外的@ParameterObject和@Parameter注解):
spring-doc:
default-flat-param-object: true
配置
配置文件,更多配置请看:springdoc 核心配置
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
swagger-ui:
# 持久化认证数据,如果设置为 true,它会保留授权数据并且不会在浏览器关闭/刷新时丢失
persistAuthorization: true
配置文档:
SpringBoot 3 在配置上区别不大,注意引入包地址的一些变化,有问题可以在评论区提出。
@Configuration
@AutoConfigureBefore(SpringDocConfiguration.class)
public class OpenApiConfig {
private static final String TOKEN_HEADER = "Authorization";
@Bean
public OpenAPI openApi() {
return new OpenAPI()
.components(
new Components().addSecuritySchemes(TOKEN_HEADER,
new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
// 这里配置 bearer 后,你的请求里会自动在 token 前加上 Bearer
.scheme("bearer")
.bearerFormat("JWT")
).addParameters(TOKEN_HEADER,
new Parameter()
.in("header")
.schema(new StringSchema())
.name(tokenHeader)
))
.info(
new Info()
.title("文档标题")
.description("文档描述")
.contact(new Contact().name("作者").email("邮箱").url("可以写你的博客地址或不填"))
// 参考 Apache 2.0 许可及地址,你可以不配此项
.license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0.html"))
.version("0.1")
)
// 引入外部的文档,我这里引得是 springdoc 官方文档地址,你可以不配此项
.externalDocs(new ExternalDocumentation()
.description("SpringDoc Full Documentation")
.url("https://springdoc.org/")
);
}
/**
* GroupedOpenApi 是对接口文档分组,类似于 swagger 的 Docket
*
* @return
*/
@Bean
public GroupedOpenApi authApi() {
return GroupedOpenApi.builder()
// 组名
.group("认证接口")
// 扫描的路径,支持通配符
.pathsToMatch("/login")
// 扫描的包
.packagesToScan("com.demo.controller.auth")
.build();
}
@Bean
public GroupedOpenApi sysApi() {
return GroupedOpenApi.builder()
.group("系统接口")
.pathsToMatch("/sys/**")
// 添加自定义配置,这里添加了一个用户认证的 header,否则 knife4j 里会没有 header
.addOperationCustomizer((operation, handlerMethod) -> operation.security(
Collections.singletonList(new SecurityRequirement().addList(TOKEN_HEADER)))
)
.build();
}
}
访问文档
配置完成之后,就可以访问文档地址:http://localhost:${port}/${context-path}/swagger-ui.html
❗ 如果你加入了拦截器或引入了spring-security等权限框架,需要放通文档地址及静态资源:
- /**/*.html
- /**/*.css
- /**/*.js
- /**/api-docs/**
至此一个简单的接口文档就生成了,是不是很简单😎
Java 注解支持
springdoc还提供了对Java 注解的支持,可以识别你的注解自动生成文档
Maven 引入
在之前我们引入springdoc-openapi-ui之后,还需要添加对javadoc的支持,具体可参考官方文档。
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
<!-- 添加 javadoc 支持 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-javadoc</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<!-- 添加对 javadoc 的支持 -->
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.15.0</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
配置好pom之后,就可以使用注解生成接口文档了,这里我举个🌰
/**
* 用户管理控制器
*
* @author penga
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/sys/user")
public class SysUserController {
private final ISysUserService userService;
/**
* 分页查询
*/
@GetMapping("/page")
public R<PageInfo<SysUserPageVo>> queryPage(SysUserPageQuery pageQuery) {
PageInfo<SysUserPageVo> page = userService.queryPage(pageQuery);
return R.ok(page);
}
}
/**
* 用户管理 分页查询对象
*
* @author penga
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SysUserPageQuery extends PageQuery {
/**
* 用户名
*/
private String username;
/**
* 真实姓名
*/
private String realName;
}
访问文档
访问接口文档,如图所示:
集成knife4j
我使用的是 Apifox,所以knife4j使用如果有问题,请自行查看 knife4j 官网。
Maven引入
在maven里引入knife4j
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
❗ 如果你使用的是SpringBoot3,需要注意:
- Spring Boot 3 只支持OpenAPI3规范
- Knife4j提供的starter已经引用springdoc-openapi的jar,同样为了解决注解冲突问题,需要重新引入
- JDK版本必须 >= 17
- 使用 Spring Boot 3 请确保引入 springdoc-openapi 2.0+
而且需要引入这个包:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
访问文档地址
然后你就可以直接访问文档地址了:http://localhost:${port}/${context-path}/doc.html
至此我们就集成完了,有其他疑问欢迎在评论区提出来😊