文章目录
1. SpringDoc 简介
SpringDoc 是一个开源工具,它集成了 OpenAPI 3 和 Swagger UI,可以自动为基于 Spring Boot 开发的 REST API 生成 API 文档。SpringDoc 替代了过去的 SpringFox,并提供了与 SpringBoot 3 更好的兼容性。
1.1 SpringDoc 优势
- 支持 OpenAPI 3 规范
- 与 SpringBoot 3 完美集成
- 自动扫描并生成 API 文档
- 支持丰富的注解来定制 API 文档
- 提供 Swagger UI 进行文档可视化
- 支持分组、安全配置等高级特性
2. 环境准备
2.1 Maven 依赖
在 SpringBoot 3 项目中添加 SpringDoc 依赖:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
对于 WebFlux 项目,使用:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>2.3.0</version>
</dependency>
2.2 基础配置
在 application.yml
中添加基础配置:
springdoc:
api-docs:
enabled: true # 启用/禁用API文档的访问
path: /v3/api-docs # 设置API文档的访问路径
swagger-ui:
path: /swagger-ui.html # 设置Swagger UI的访问路径
disable-swagger-default-url: true
display-request-duration: true # 显示请求持续时间
packages-to-scan: com.example.controller # 指定要扫描的包
paths-to-match: /api/**, /public/** # 指定要匹配的路径
3. 创建基本文档配置类
创建一个配置类来自定义 API 文档:
package com.example.config;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("我的API文档")
.description("Spring Boot 3 应用接口文档")
.version("v1.0.0")
.contact(new Contact()
.name("开发者")
.email("developer@example.com")
.url("https://www.example.com"))
.license(new License()
.name("Apache 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0")))
.externalDocs(new ExternalDocumentation()
.description("更多文档")
.url("https://springdoc.org"));
}
}
4. 控制器 API 文档注解
4.1 基本控制器示例
package com.example.controller;
import com.example.model.User;
import com.example.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
@Tag(name = "用户管理", description = "用户管理相关接口")
public class UserController {
@Autowired
private UserService userService;
@Operation(summary = "获取所有用户", description = "返回系统中所有用户的列表")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取用户列表"),
@ApiResponse(responseCode = "401", description = "未授权"),
@ApiResponse(responseCode = "403", description = "禁止访问"),
@ApiResponse(responseCode = "500", description = "服务器错误")
})
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userService.findAllUsers());
}
@Operation(summary = "通过ID获取用户", description = "根据ID查询并返回一个用户")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功找到用户",
content = @Content(schema = @Schema(implementation = User.class))),
@ApiResponse(responseCode = "404", description = "用户不存在")
})
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(
@Parameter(description = "用户ID", required = true) @PathVariable Long id) {
return ResponseEntity.ok(userService.findUserById(id));
}
@Operation(summary = "创建新用户", description = "创建一个新用户并返回")
@PostMapping
public ResponseEntity<User> createUser(
@Parameter(description = "用户信息", required = true) @RequestBody User user) {
return ResponseEntity.ok(userService.saveUser(user));
}
@Operation(summary = "更新用户", description = "更新指定ID的用户信息")
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@Parameter(description = "用户ID", required = true) @PathVariable Long id,
@Parameter(description = "更新后的用户信息", required = true) @RequestBody User user) {
return ResponseEntity.ok(userService.updateUser(id, user));
}
@Operation(summary = "删除用户", description = "根据ID删除用户")
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(
@Parameter(description = "要删除的用户ID", required = true) @PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.ok().build();
}
}
4.2 模型类示例
package com.example.model;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
@Schema(description = "用户信息")
public class User {
@Schema(description = "用户ID", example = "1")
private Long id;
@Schema(description = "用户名", example = "zhangsan", required = true)
@NotBlank(message = "用户名不能为空")
@Size(min = 4, max = 20, message = "用户名长度必须在4-20之间")
private String username;
@Schema(description = "用户邮箱", example = "zhangsan@example.com")
@Email(message = "邮箱格式不正确")
private String email;
@Schema(description = "用户年龄", example = "25")
private Integer age;
// 构造函数、Getter和Setter方法省略
}
5. 高级功能
5.1 API分组
当你的API数量较多时,可以将它们分组展示:
package com.example.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public-api")
.pathsToMatch("/api/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin-api")
.pathsToMatch("/api/admin/**")
.build();
}
@Bean
public GroupedOpenApi userApi() {
return GroupedOpenApi.builder()
.group("user-api")
.pathsToMatch("/api/users/**")
.build();
}
}
5.2 安全配置
为API添加安全配置(例如JWT令牌):
package com.example.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
final String securitySchemeName = "bearerAuth";
return new OpenAPI()
.info(new Info()
.title("安全API文档")
.version("1.0.0"))
.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
.components(new Components()
.addSecuritySchemes(securitySchemeName,
new SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("请输入JWT令牌")));
}
}
在控制器中使用:
@RestController
@RequestMapping("/api/secured")
@SecurityRequirement(name = "bearerAuth")
@Tag(name = "安全接口", description = "需要认证才能访问的接口")
public class SecuredController {
@Operation(summary = "受保护的接口", description = "这个接口需要JWT认证")
@GetMapping("/data")
public ResponseEntity<String> getSecuredData() {
return ResponseEntity.ok("这是受保护的数据");
}
}
5.3 隐藏特定端点
有时你可能不想在文档中显示某些端点:
@Hidden // 隐藏整个控制器
@RestController
@RequestMapping("/api/internal")
public class InternalController {
@GetMapping("/status")
public String getStatus() {
return "OK";
}
}
或者隐藏特定方法:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Operation(summary = "公开接口")
@GetMapping("/public")
public String publicEndpoint() {
return "这是公开的";
}
@Hidden // 隐藏此方法
@GetMapping("/internal")
public String internalEndpoint() {
return "这是内部接口";
}
}
6. 参数描述
6.1 路径参数
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(
@Parameter(description = "用户ID", example = "1", required = true)
@PathVariable Long id) {
// 实现代码
}
6.2 查询参数
@GetMapping
public ResponseEntity<List<User>> searchUsers(
@Parameter(description = "用户名(支持模糊查询)", example = "zhang")
@RequestParam(required = false) String username,
@Parameter(description = "用户年龄(精确匹配)", example = "25")
@RequestParam(required = false) Integer age,
@Parameter(description = "排序字段", example = "username", schema = @Schema(allowableValues = {"id", "username", "age"}))
@RequestParam(defaultValue = "id") String sortBy) {
// 实现代码
}
6.3 请求体
@PostMapping
public ResponseEntity<User> createUser(
@Parameter(description = "用户信息", required = true)
@RequestBody User user) {
// 实现代码
}
7. 响应文档化
7.1 基本响应
@Operation(summary = "获取用户")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取用户"),
@ApiResponse(responseCode = "404", description = "用户不存在"),
@ApiResponse(responseCode = "500", description = "服务器错误")
})
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
// 实现代码
}
7.2 详细响应内容
@Operation(summary = "获取用户")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取用户",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = User.class))),
@ApiResponse(responseCode = "404", description = "用户不存在",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "服务器错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
// 实现代码
}
7.3 自定义响应模型
@Schema(description = "错误响应")
public class ErrorResponse {
@Schema(description = "HTTP状态码", example = "404")
private int status;
@Schema(description = "错误消息", example = "用户不存在")
private String message;
@Schema(description = "错误发生时间", example = "2023-08-15T14:30:15.123Z")
private String timestamp;
// 构造函数、Getter和Setter方法省略
}
8. 访问文档
配置好后,可以通过以下URL访问接口文档:
- API文档JSON: http://localhost:8080/v3/api-docs
- Swagger UI界面: http://localhost:8080/swagger-ui.html
9. 常见问题及最佳实践
9.1 常见问题
-
文档未显示:检查包扫描配置是否正确,控制器是否有相应注解。
-
自定义模型未显示:确保模型类使用了
@Schema
注解,并且在控制器中引用。 -
日期格式不正确:配置全局日期格式转换器。
9.2 最佳实践
-
保持文档更新:确保代码变更时同步更新文档注解。
-
使用分组:当API数量增多时,适当分组以提高文档可读性。
-
添加足够的描述:为每个接口、参数和响应添加清晰的描述。
-
使用示例值:为参数和响应字段提供示例值,帮助调用者理解。
-
版本控制:明确标注API版本信息。
-
安全考虑:在生产环境中,考虑是否需要限制文档访问或完全禁用。
10. 完整示例
下面是一个完整的SpringBoot 3+SpringDoc项目结构示例:
src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── Application.java
│ │ ├── config/
│ │ │ └── OpenApiConfig.java
│ │ ├── controller/
│ │ │ ├── UserController.java
│ │ │ └── ProductController.java
│ │ ├── model/
│ │ │ ├── User.java
│ │ │ └── Product.java
│ │ └── service/
│ │ ├── UserService.java
│ │ └── ProductService.java
│ └── resources/
│ └── application.yml
└── test/
lication.java
│ │ ├── config/
│ │ │ └── OpenApiConfig.java
│ │ ├── controller/
│ │ │ ├── UserController.java
│ │ │ └── ProductController.java
│ │ ├── model/
│ │ │ ├── User.java
│ │ │ └── Product.java
│ │ └── service/
│ │ ├── UserService.java
│ │ └── ProductService.java
│ └── resources/
│ └── application.yml
└── test/
这个项目结构组织清晰,便于维护和扩展。通过合理使用SpringDoc的注解和配置,可以为API创建详细、易用的文档。