|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
Swagger是一个强大且广泛使用的API开发工具集,它使得设计和文档化RESTful API变得更加简单。通过Swagger注解,开发者可以直接在代码中描述API,从而自动生成交互式API文档,提高开发效率和团队协作能力。本文将深入浅出地介绍Swagger注解的使用,从基础概念到高级应用,帮助你全面掌握Swagger,提升API开发能力。
Swagger基础知识
Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码中,允许API始终保持同步。
在Java项目中,我们通常使用Springfox或SpringDoc来实现Swagger集成。Springfox适用于较早的Spring Boot版本,而SpringDoc则支持Spring Boot 2.x和3.x以及OpenAPI 3规范。本文将以SpringDoc为例进行讲解,因为它代表了未来的发展方向。
首先,我们需要在项目中添加Swagger依赖:
- <!-- 对于Maven项目 -->
- <dependency>
- <groupId>org.springdoc</groupId>
- <artifactId>springdoc-openapi-ui</artifactId>
- <version>1.6.14</version>
- </dependency>
复制代码
或者对于Gradle项目:
- implementation 'org.springdoc:springdoc-openapi-ui:1.6.14'
复制代码
添加依赖后,我们可以通过访问http://localhost:8080/swagger-ui.html来查看Swagger UI界面。
Swagger常用注解详解
Swagger提供了丰富的注解来描述API。这些注解可以分为类级别、方法级别、参数级别和模型级别。下面我们详细介绍这些注解及其用法。
类级别注解
@Tag注解用于在控制器类上,为API分组提供名称和描述。
- import io.swagger.v3.oas.annotations.tags.Tag;
- @Tag(name = "用户管理", description = "用户相关的API操作")
- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- // 控制器方法
- }
复制代码
在Swagger UI中,这个控制器下的所有API都会被归类到”用户管理”标签下,并显示描述信息。
@SecurityScheme注解用于定义安全方案,如API密钥、OAuth2等。
- import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
- import io.swagger.v3.oas.annotations.security.SecurityScheme;
- @SecurityScheme(
- name = "bearerAuth",
- type = SecuritySchemeType.HTTP,
- bearerFormat = "JWT",
- scheme = "bearer"
- )
- @SpringBootApplication
- public class MyApp {
- public static void main(String[] args) {
- SpringApplication.run(MyApp.class, args);
- }
- }
复制代码
这个配置定义了一个名为”bearerAuth”的JWT认证方案。
方法级别注解
@Operation注解用于描述单个API操作,包括摘要、详细描述等。
- import io.swagger.v3.oas.annotations.Operation;
- import io.swagger.v3.oas.annotations.responses.ApiResponse;
- import io.swagger.v3.oas.annotations.responses.ApiResponses;
- @Operation(
- summary = "获取用户信息",
- description = "根据用户ID获取用户的详细信息",
- tags = { "用户管理" }
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取用户信息"),
- @ApiResponse(responseCode = "404", description = "用户不存在"),
- @ApiResponse(responseCode = "500", description = "服务器内部错误")
- })
- @GetMapping("/{id}")
- public ResponseEntity<User> getUserById(@PathVariable Long id) {
- // 方法实现
- }
复制代码
@ApiResponse注解用于描述API可能的响应。通常与@ApiResponses一起使用,可以定义多个响应。
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取用户信息",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = User.class)) }),
- @ApiResponse(responseCode = "404", description = "用户不存在",
- content = @Content),
- @ApiResponse(responseCode = "500", description = "服务器内部错误",
- content = @Content)
- })
- @GetMapping("/{id}")
- public ResponseEntity<User> getUserById(@PathVariable Long id) {
- // 方法实现
- }
复制代码
@Parameters注解用于描述API的所有参数。当有多个参数需要描述时,可以使用此注解。
- import io.swagger.v3.oas.annotations.Parameter;
- import io.swagger.v3.oas.annotations.Parameters;
- @Parameters({
- @Parameter(name = "id", description = "用户ID", required = true, example = "1"),
- @Parameter(name = "fields", description = "需要返回的字段,多个字段用逗号分隔",
- required = false, example = "id,name,email")
- })
- @GetMapping("/{id}")
- public ResponseEntity<User> getUserById(
- @PathVariable Long id,
- @RequestParam(required = false) String fields) {
- // 方法实现
- }
复制代码
参数级别注解
@Parameter注解用于描述方法参数,包括名称、描述、是否必需等。
- @GetMapping("/search")
- public ResponseEntity<List<User>> searchUsers(
- @Parameter(description = "搜索关键词", required = true, example = "john")
- @RequestParam String keyword,
-
- @Parameter(description = "页码", required = false, example = "1")
- @RequestParam(defaultValue = "1") int page,
-
- @Parameter(description = "每页大小", required = false, example = "10")
- @RequestParam(defaultValue = "10") int size) {
- // 方法实现
- }
复制代码
@RequestBody注解与@Content和@Schema结合使用,用于描述请求体。
- import io.swagger.v3.oas.annotations.media.Content;
- import io.swagger.v3.oas.annotations.media.Schema;
- import io.swagger.v3.oas.annotations.parameters.RequestBody;
- @Operation(summary = "创建用户", description = "创建一个新的用户")
- @PostMapping("/")
- public ResponseEntity<User> createUser(
- @RequestBody(description = "用户信息", required = true,
- content = @Content(
- mediaType = "application/json",
- schema = @Schema(implementation = UserCreateRequest.class)
- )
- )
- @Valid @RequestBody UserCreateRequest userRequest) {
- // 方法实现
- }
复制代码
模型注解
@Schema注解用于描述模型类及其属性。
- import io.swagger.v3.oas.annotations.media.Schema;
- @Schema(description = "用户信息")
- public class User {
-
- @Schema(description = "用户ID", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
- private Long id;
-
- @Schema(description = "用户名", example = "john_doe", requiredMode = Schema.RequiredMode.REQUIRED)
- private String username;
-
- @Schema(description = "电子邮箱", example = "john.doe@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
- private String email;
-
- @Schema(description = "创建时间", example = "2023-01-01T12:00:00Z", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private LocalDateTime createdAt;
-
- // getters and setters
- }
复制代码
@ArraySchema注解用于描述数组类型的模型。
- @ArraySchema(schema = @Schema(implementation = User.class))
- private List<User> users;
复制代码
实际应用场景与示例
场景一:构建用户管理API
让我们通过一个完整的用户管理API示例,展示如何在实际项目中应用Swagger注解。
首先,定义用户模型:
- import io.swagger.v3.oas.annotations.media.Schema;
- import java.time.LocalDateTime;
- @Schema(description = "用户信息")
- public class User {
-
- @Schema(description = "用户ID", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
- private Long id;
-
- @Schema(description = "用户名", example = "john_doe", requiredMode = Schema.RequiredMode.REQUIRED)
- private String username;
-
- @Schema(description = "电子邮箱", example = "john.doe@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
- private String email;
-
- @Schema(description = "手机号码", example = "13800138000", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String phone;
-
- @Schema(description = "用户状态", example = "ACTIVE", requiredMode = Schema.RequiredMode.REQUIRED)
- private UserStatus status;
-
- @Schema(description = "创建时间", example = "2023-01-01T12:00:00Z", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private LocalDateTime createdAt;
-
- @Schema(description = "更新时间", example = "2023-01-01T12:00:00Z", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private LocalDateTime updatedAt;
-
- // 枚举定义用户状态
- public enum UserStatus {
- ACTIVE, INACTIVE, DISABLED
- }
-
- // getters and setters
- }
复制代码
然后,定义创建用户的请求DTO:
- import io.swagger.v3.oas.annotations.media.Schema;
- import javax.validation.constraints.Email;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Pattern;
- import javax.validation.constraints.Size;
- @Schema(description = "创建用户请求")
- public class UserCreateRequest {
-
- @NotBlank(message = "用户名不能为空")
- @Size(min = 3, max = 20, message = "用户名长度必须在3到20个字符之间")
- @Schema(description = "用户名", example = "john_doe", requiredMode = Schema.RequiredMode.REQUIRED)
- private String username;
-
- @NotBlank(message = "电子邮箱不能为空")
- @Email(message = "电子邮箱格式不正确")
- @Schema(description = "电子邮箱", example = "john.doe@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
- private String email;
-
- @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号码格式不正确")
- @Schema(description = "手机号码", example = "13800138000", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String phone;
-
- @Schema(description = "密码", example = "Password123!", requiredMode = Schema.RequiredMode.REQUIRED)
- private String password;
-
- // getters and setters
- }
复制代码
定义更新用户的请求DTO:
- import io.swagger.v3.oas.annotations.media.Schema;
- @Schema(description = "更新用户请求")
- public class UserUpdateRequest {
-
- @Schema(description = "电子邮箱", example = "john.doe@example.com", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String email;
-
- @Schema(description = "手机号码", example = "13800138000", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String phone;
-
- @Schema(description = "用户状态", example = "ACTIVE", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private User.UserStatus status;
-
- // getters and setters
- }
复制代码
现在,创建用户控制器:
- 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.security.SecurityRequirement;
- import io.swagger.v3.oas.annotations.tags.Tag;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import javax.validation.Valid;
- import java.util.List;
- @Tag(name = "用户管理", description = "用户相关的API操作")
- @RestController
- @RequestMapping("/api/users")
- @SecurityRequirement(name = "bearerAuth")
- public class UserController {
- private final UserService userService;
- public UserController(UserService userService) {
- this.userService = userService;
- }
- @Operation(
- summary = "创建用户",
- description = "创建一个新的用户账户"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "201", description = "用户创建成功",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = User.class)) }),
- @ApiResponse(responseCode = "400", description = "请求参数不正确",
- content = @Content),
- @ApiResponse(responseCode = "409", description = "用户名或邮箱已存在",
- content = @Content)
- })
- @PostMapping("/")
- public ResponseEntity<User> createUser(
- @Parameter(description = "用户信息", required = true)
- @Valid @RequestBody UserCreateRequest userRequest) {
- User createdUser = userService.createUser(userRequest);
- return ResponseEntity.status(201).body(createdUser);
- }
- @Operation(
- summary = "获取用户",
- description = "根据用户ID获取用户详细信息"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取用户信息",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = User.class)) }),
- @ApiResponse(responseCode = "404", description = "用户不存在",
- content = @Content)
- })
- @GetMapping("/{id}")
- public ResponseEntity<User> getUserById(
- @Parameter(description = "用户ID", required = true, example = "1")
- @PathVariable Long id) {
- User user = userService.getUserById(id);
- return ResponseEntity.ok(user);
- }
- @Operation(
- summary = "更新用户",
- description = "根据用户ID更新用户信息"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "用户更新成功",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = User.class)) }),
- @ApiResponse(responseCode = "400", description = "请求参数不正确",
- content = @Content),
- @ApiResponse(responseCode = "404", description = "用户不存在",
- content = @Content)
- })
- @PutMapping("/{id}")
- public ResponseEntity<User> updateUser(
- @Parameter(description = "用户ID", required = true, example = "1")
- @PathVariable Long id,
- @Parameter(description = "更新信息", required = true)
- @Valid @RequestBody UserUpdateRequest userRequest) {
- User updatedUser = userService.updateUser(id, userRequest);
- return ResponseEntity.ok(updatedUser);
- }
- @Operation(
- summary = "删除用户",
- description = "根据用户ID删除用户"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "204", description = "用户删除成功",
- content = @Content),
- @ApiResponse(responseCode = "404", description = "用户不存在",
- content = @Content)
- })
- @DeleteMapping("/{id}")
- public ResponseEntity<Void> deleteUser(
- @Parameter(description = "用户ID", required = true, example = "1")
- @PathVariable Long id) {
- userService.deleteUser(id);
- return ResponseEntity.noContent().build();
- }
- @Operation(
- summary = "获取用户列表",
- description = "分页获取用户列表,支持按关键词搜索"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取用户列表",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = PageResult.class)) })
- })
- @GetMapping("/")
- public ResponseEntity<PageResult<User>> getUsers(
- @Parameter(description = "页码", required = false, example = "1")
- @RequestParam(defaultValue = "1") int page,
-
- @Parameter(description = "每页大小", required = false, example = "10")
- @RequestParam(defaultValue = "10") int size,
-
- @Parameter(description = "搜索关键词", required = false, example = "john")
- @RequestParam(required = false) String keyword,
-
- @Parameter(description = "用户状态过滤", required = false, example = "ACTIVE")
- @RequestParam(required = false) User.UserStatus status) {
-
- PageResult<User> users = userService.getUsers(page, size, keyword, status);
- return ResponseEntity.ok(users);
- }
- }
复制代码
最后,定义分页结果模型:
- import io.swagger.v3.oas.annotations.media.Schema;
- import java.util.List;
- @Schema(description = "分页结果")
- public class PageResult<T> {
-
- @Schema(description = "数据列表", requiredMode = Schema.RequiredMode.REQUIRED)
- private List<T> content;
-
- @Schema(description = "当前页码", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
- private int pageNumber;
-
- @Schema(description = "每页大小", example = "10", requiredMode = Schema.RequiredMode.REQUIRED)
- private int pageSize;
-
- @Schema(description = "总记录数", example = "100", requiredMode = Schema.RequiredMode.REQUIRED)
- private long totalElements;
-
- @Schema(description = "总页数", example = "10", requiredMode = Schema.RequiredMode.REQUIRED)
- private int totalPages;
-
- @Schema(description = "是否有下一页", example = "true", requiredMode = Schema.RequiredMode.REQUIRED)
- private boolean hasNext;
-
- @Schema(description = "是否有上一页", example = "false", requiredMode = Schema.RequiredMode.REQUIRED)
- private boolean hasPrevious;
-
- // getters and setters
- }
复制代码
场景二:构建文件上传API
文件上传是Web应用中常见的功能,下面展示如何使用Swagger注解描述文件上传API。
- 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.tags.Tag;
- import org.springframework.http.MediaType;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import org.springframework.web.multipart.MultipartFile;
- import java.util.HashMap;
- import java.util.Map;
- @Tag(name = "文件管理", description = "文件上传和下载相关的API操作")
- @RestController
- @RequestMapping("/api/files")
- @SecurityRequirement(name = "bearerAuth")
- public class FileController {
- @Operation(
- summary = "上传文件",
- description = "上传单个文件,支持图片、文档等格式"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "文件上传成功",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = FileUploadResult.class)) }),
- @ApiResponse(responseCode = "400", description = "请求参数不正确",
- content = @Content)
- })
- @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public ResponseEntity<FileUploadResult> uploadFile(
- @Parameter(description = "要上传的文件", required = true)
- @RequestParam("file") MultipartFile file,
-
- @Parameter(description = "文件分类", required = false, example = "image")
- @RequestParam(required = false) String category) {
-
- // 模拟文件上传处理
- String fileId = "file_" + System.currentTimeMillis();
- String fileName = file.getOriginalFilename();
- long fileSize = file.getSize();
- String contentType = file.getContentType();
-
- FileUploadResult result = new FileUploadResult(fileId, fileName, fileSize, contentType, category);
- return ResponseEntity.ok(result);
- }
- @Operation(
- summary = "批量上传文件",
- description = "一次上传多个文件"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "文件上传成功",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = FileUploadResult.class)) }),
- @ApiResponse(responseCode = "400", description = "请求参数不正确",
- content = @Content)
- })
- @PostMapping(value = "/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public ResponseEntity<List<FileUploadResult>> batchUploadFiles(
- @Parameter(description = "要上传的文件列表", required = true)
- @RequestParam("files") MultipartFile[] files,
-
- @Parameter(description = "文件分类", required = false, example = "image")
- @RequestParam(required = false) String category) {
-
- // 模拟批量文件上传处理
- List<FileUploadResult> results = new ArrayList<>();
- for (MultipartFile file : files) {
- String fileId = "file_" + System.currentTimeMillis();
- String fileName = file.getOriginalFilename();
- long fileSize = file.getSize();
- String contentType = file.getContentType();
-
- results.add(new FileUploadResult(fileId, fileName, fileSize, contentType, category));
- }
-
- return ResponseEntity.ok(results);
- }
- @Operation(
- summary = "下载文件",
- description = "根据文件ID下载文件"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "文件下载成功",
- content = { @Content(mediaType = "application/octet-stream") }),
- @ApiResponse(responseCode = "404", description = "文件不存在",
- content = @Content)
- })
- @GetMapping("/download/{fileId}")
- public ResponseEntity<byte[]> downloadFile(
- @Parameter(description = "文件ID", required = true, example = "file_123456789")
- @PathVariable String fileId) {
-
- // 模拟文件下载处理
- byte[] fileContent = "Mock file content".getBytes();
- String filename = "example.txt";
-
- return ResponseEntity.ok()
- .header("Content-Disposition", "attachment; filename="" + filename + """)
- .contentType(MediaType.APPLICATION_OCTET_STREAM)
- .body(fileContent);
- }
- }
- // 文件上传结果模型
- @Schema(description = "文件上传结果")
- class FileUploadResult {
-
- @Schema(description = "文件ID", example = "file_123456789", requiredMode = Schema.RequiredMode.REQUIRED)
- private String fileId;
-
- @Schema(description = "文件名", example = "example.jpg", requiredMode = Schema.RequiredMode.REQUIRED)
- private String fileName;
-
- @Schema(description = "文件大小(字节)", example = "10240", requiredMode = Schema.RequiredMode.REQUIRED)
- private long fileSize;
-
- @Schema(description = "文件类型", example = "image/jpeg", requiredMode = Schema.RequiredMode.REQUIRED)
- private String contentType;
-
- @Schema(description = "文件分类", example = "image", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String category;
-
- @Schema(description = "文件URL", example = "http://example.com/files/file_123456789", requiredMode = Schema.RequiredMode.REQUIRED)
- private String url;
-
- public FileUploadResult(String fileId, String fileName, long fileSize, String contentType, String category) {
- this.fileId = fileId;
- this.fileName = fileName;
- this.fileSize = fileSize;
- this.contentType = contentType;
- this.category = category;
- this.url = "http://example.com/files/" + fileId;
- }
-
- // getters and setters
- }
复制代码
场景三:构建带权限控制的API
在实际应用中,很多API需要权限控制。下面展示如何使用Swagger注解描述带权限控制的API。
首先,定义角色枚举:
- import io.swagger.v3.oas.annotations.media.Schema;
- @Schema(description = "用户角色")
- public enum UserRole {
- @Schema(description = "管理员")
- ADMIN,
-
- @Schema(description = "普通用户")
- USER,
-
- @Schema(description = "访客")
- GUEST
- }
复制代码
然后,创建带权限控制的API控制器:
- import io.swagger.v3.oas.annotations.Operation;
- import io.swagger.v3.oas.annotations.Parameter;
- import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
- import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
- 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.security.SecurityRequirement;
- import io.swagger.v3.oas.annotations.security.SecurityScheme;
- import io.swagger.v3.oas.annotations.tags.Tag;
- import org.springframework.http.ResponseEntity;
- import org.springframework.security.access.prepost.PreAuthorize;
- import org.springframework.web.bind.annotation.*;
- import java.util.Arrays;
- import java.util.List;
- @SecurityScheme(
- name = "bearerAuth",
- type = SecuritySchemeType.HTTP,
- scheme = "bearer",
- bearerFormat = "JWT",
- in = SecuritySchemeIn.HEADER,
- description = "JWT认证"
- )
- @Tag(name = "系统管理", description = "系统管理相关的API操作")
- @RestController
- @RequestMapping("/api/admin")
- @SecurityRequirement(name = "bearerAuth")
- public class AdminController {
- @Operation(
- summary = "获取系统配置",
- description = "获取系统全局配置信息,仅管理员可访问"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取系统配置",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = SystemConfig.class)) }),
- @ApiResponse(responseCode = "403", description = "权限不足",
- content = @Content)
- })
- @PreAuthorize("hasRole('ADMIN')")
- @GetMapping("/config")
- public ResponseEntity<SystemConfig> getSystemConfig() {
- // 模拟获取系统配置
- SystemConfig config = new SystemConfig(
- "My Application",
- "1.0.0",
- Arrays.asList("en", "zh", "ja"),
- true,
- 100
- );
- return ResponseEntity.ok(config);
- }
- @Operation(
- summary = "更新系统配置",
- description = "更新系统全局配置信息,仅管理员可访问"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功更新系统配置",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = SystemConfig.class)) }),
- @ApiResponse(responseCode = "400", description = "请求参数不正确",
- content = @Content),
- @ApiResponse(responseCode = "403", description = "权限不足",
- content = @Content)
- })
- @PreAuthorize("hasRole('ADMIN')")
- @PutMapping("/config")
- public ResponseEntity<SystemConfig> updateSystemConfig(
- @Parameter(description = "系统配置信息", required = true)
- @Valid @RequestBody SystemConfigUpdateRequest configRequest) {
-
- // 模拟更新系统配置
- SystemConfig updatedConfig = new SystemConfig(
- configRequest.getAppName(),
- configRequest.getVersion(),
- configRequest.getSupportedLanguages(),
- configRequest.isMaintenanceMode(),
- configRequest.getMaxUploadSize()
- );
- return ResponseEntity.ok(updatedConfig);
- }
- @Operation(
- summary = "获取用户列表",
- description = "分页获取系统所有用户,管理员可访问"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取用户列表",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = PageResult.class)) }),
- @ApiResponse(responseCode = "403", description = "权限不足",
- content = @Content)
- })
- @PreAuthorize("hasRole('ADMIN')")
- @GetMapping("/users")
- public ResponseEntity<PageResult<User>> getUsers(
- @Parameter(description = "页码", required = false, example = "1")
- @RequestParam(defaultValue = "1") int page,
-
- @Parameter(description = "每页大小", required = false, example = "10")
- @RequestParam(defaultValue = "10") int size,
-
- @Parameter(description = "角色过滤", required = false, example = "USER")
- @RequestParam(required = false) UserRole role) {
-
- // 模拟获取用户列表
- List<User> users = Arrays.asList(
- new User(1L, "admin", "admin@example.com", UserRole.ADMIN),
- new User(2L, "user1", "user1@example.com", UserRole.USER),
- new User(3L, "user2", "user2@example.com", UserRole.USER)
- );
-
- PageResult<User> result = new PageResult<>(
- users,
- page,
- size,
- users.size(),
- (int) Math.ceil((double) users.size() / size),
- page < Math.ceil((double) users.size() / size),
- page > 1
- );
-
- return ResponseEntity.ok(result);
- }
- @Operation(
- summary = "更新用户角色",
- description = "更新指定用户的角色,仅管理员可访问"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功更新用户角色",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = User.class)) }),
- @ApiResponse(responseCode = "404", description = "用户不存在",
- content = @Content),
- @ApiResponse(responseCode = "403", description = "权限不足",
- content = @Content)
- })
- @PreAuthorize("hasRole('ADMIN')")
- @PutMapping("/users/{userId}/role")
- public ResponseEntity<User> updateUserRole(
- @Parameter(description = "用户ID", required = true, example = "1")
- @PathVariable Long userId,
-
- @Parameter(description = "用户角色", required = true, example = "ADMIN")
- @RequestParam UserRole role) {
-
- // 模拟更新用户角色
- User updatedUser = new User(userId, "user", "user@example.com", role);
- return ResponseEntity.ok(updatedUser);
- }
- }
- // 系统配置模型
- @Schema(description = "系统配置")
- class SystemConfig {
-
- @Schema(description = "应用名称", example = "My Application", requiredMode = Schema.RequiredMode.REQUIRED)
- private String appName;
-
- @Schema(description = "应用版本", example = "1.0.0", requiredMode = Schema.RequiredMode.REQUIRED)
- private String version;
-
- @Schema(description = "支持的语言列表", requiredMode = Schema.RequiredMode.REQUIRED)
- private List<String> supportedLanguages;
-
- @Schema(description = "是否处于维护模式", example = "false", requiredMode = Schema.RequiredMode.REQUIRED)
- private boolean maintenanceMode;
-
- @Schema(description = "最大上传大小(MB)", example = "100", requiredMode = Schema.RequiredMode.REQUIRED)
- private int maxUploadSize;
-
- public SystemConfig(String appName, String version, List<String> supportedLanguages,
- boolean maintenanceMode, int maxUploadSize) {
- this.appName = appName;
- this.version = version;
- this.supportedLanguages = supportedLanguages;
- this.maintenanceMode = maintenanceMode;
- this.maxUploadSize = maxUploadSize;
- }
-
- // getters and setters
- }
- // 系统配置更新请求模型
- @Schema(description = "系统配置更新请求")
- class SystemConfigUpdateRequest {
-
- @Schema(description = "应用名称", example = "My Application", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String appName;
-
- @Schema(description = "应用版本", example = "1.0.0", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private String version;
-
- @Schema(description = "支持的语言列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private List<String> supportedLanguages;
-
- @Schema(description = "是否处于维护模式", example = "false", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private Boolean maintenanceMode;
-
- @Schema(description = "最大上传大小(MB)", example = "100", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private Integer maxUploadSize;
-
- // getters and setters
- }
- // 用户模型(简化版)
- @Schema(description = "用户信息")
- class User {
-
- @Schema(description = "用户ID", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
- private Long id;
-
- @Schema(description = "用户名", example = "john_doe", requiredMode = Schema.RequiredMode.REQUIRED)
- private String username;
-
- @Schema(description = "电子邮箱", example = "john.doe@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
- private String email;
-
- @Schema(description = "用户角色", example = "USER", requiredMode = Schema.RequiredMode.REQUIRED)
- private UserRole role;
-
- public User(Long id, String username, String email, UserRole role) {
- this.id = id;
- this.username = username;
- this.email = email;
- this.role = role;
- }
-
- // getters and setters
- }
复制代码
高级技巧与最佳实践
1. 分组管理API
在大型项目中,API数量可能很多,合理分组管理API非常重要。可以使用@Tag注解对API进行分组。
- // 用户管理API组
- @Tag(name = "用户管理", description = "用户相关的API操作")
- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- // ...
- }
- // 产品管理API组
- @Tag(name = "产品管理", description = "产品相关的API操作")
- @RestController
- @RequestMapping("/api/products")
- public class ProductController {
- // ...
- }
- // 订单管理API组
- @Tag(name = "订单管理", description = "订单相关的API操作")
- @RestController
- @RequestMapping("/api/orders")
- public class OrderController {
- // ...
- }
复制代码
2. 自定义响应模型
有时API的响应模型比较复杂,可以使用@Schema注解自定义响应模型。
- @Schema(description = "API通用响应")
- public class ApiResponse<T> {
-
- @Schema(description = "响应码", example = "200", requiredMode = Schema.RequiredMode.REQUIRED)
- private int code;
-
- @Schema(description = "响应消息", example = "操作成功", requiredMode = Schema.RequiredMode.REQUIRED)
- private String message;
-
- @Schema(description = "响应数据", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
- private T data;
-
- @Schema(description = "时间戳", example = "2023-01-01T12:00:00Z", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime timestamp;
-
- public ApiResponse(int code, String message, T data) {
- this.code = code;
- this.message = message;
- this.data = data;
- this.timestamp = LocalDateTime.now();
- }
-
- // getters and setters
- }
复制代码
然后在控制器中使用:
- @Operation(
- summary = "获取用户",
- description = "根据用户ID获取用户详细信息"
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "成功获取用户信息",
- content = { @Content(mediaType = "application/json",
- schema = @Schema(implementation = ApiResponse.class)) }),
- @ApiResponse(responseCode = "404", description = "用户不存在",
- content = @Content)
- })
- @GetMapping("/{id}")
- public ResponseEntity<ApiResponse<User>> getUserById(@PathVariable Long id) {
- User user = userService.getUserById(id);
- ApiResponse<User> response = new ApiResponse<>(200, "成功获取用户信息", user);
- return ResponseEntity.ok(response);
- }
复制代码
3. 使用枚举限制参数值
对于某些参数,其值只能是预定义的几个选项,可以使用枚举来限制。
- @Schema(description = "排序方式")
- public enum SortOrder {
- @Schema(description = "升序")
- ASC,
-
- @Schema(description = "降序")
- DESC
- }
复制代码
然后在控制器中使用:
- @GetMapping("/users")
- public ResponseEntity<List<User>> getUsers(
- @Parameter(description = "排序字段", required = false, example = "createdAt")
- @RequestParam(required = false) String sortBy,
-
- @Parameter(description = "排序方式", required = false, example = "DESC")
- @RequestParam(required = false) SortOrder sortOrder) {
-
- List<User> users = userService.getUsers(sortBy, sortOrder);
- return ResponseEntity.ok(users);
- }
复制代码
4. 使用@Hidden隐藏API
某些内部API或测试API不希望在Swagger UI中显示,可以使用@Hidden注解隐藏它们。
- import io.swagger.v3.oas.annotations.Hidden;
- @Hidden
- @GetMapping("/internal/health-check")
- public ResponseEntity<String> healthCheck() {
- return ResponseEntity.ok("OK");
- }
复制代码
5. 使用@ExternalDocs添加外部文档
对于复杂的API,可能需要额外的文档说明,可以使用@ExternalDocs注解添加外部文档链接。
- import io.swagger.v3.oas.annotations.ExternalDocumentation;
- import io.swagger.v3.oas.annotations.extensions.Extension;
- import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
- @Tag(
- name = "支付管理",
- description = "支付相关的API操作",
- externalDocs = @ExternalDocumentation(
- description = "支付集成文档",
- url = "https://example.com/docs/payment-integration"
- )
- )
- @RestController
- @RequestMapping("/api/payments")
- public class PaymentController {
- // ...
- }
复制代码
6. 使用@Extension添加自定义扩展
Swagger支持通过@Extension注解添加自定义扩展,这些扩展可以用于添加额外的元数据。
- @Operation(
- summary = "处理支付",
- description = "处理用户支付请求",
- extensions = {
- @Extension(
- name = "x-rate-limit",
- properties = {
- @ExtensionProperty(name = "requests", value = "100"),
- @ExtensionProperty(name = "period", value = "1h")
- }
- ),
- @Extension(
- name = "x-cost",
- properties = {
- @ExtensionProperty(name = "credits", value = "5")
- }
- )
- }
- )
- @PostMapping("/process")
- public ResponseEntity<PaymentResult> processPayment(@Valid @RequestBody PaymentRequest request) {
- // ...
- }
复制代码
常见问题与解决方案
1. Swagger UI无法访问
问题:添加Swagger依赖后,访问/swagger-ui.html返回404错误。
解决方案:
1. 确认是否正确添加了依赖:<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
2. 对于Spring Boot 3.x,需要使用SpringDoc 2.x版本:<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>
3. 确认应用程序的主类上有@SpringBootApplication注解。
4. 尝试访问不同的路径:SpringDoc 1.x:/swagger-ui.htmlSpringDoc 2.x:/swagger-ui/index.html
5. SpringDoc 1.x:/swagger-ui.html
6. SpringDoc 2.x:/swagger-ui/index.html
确认是否正确添加了依赖:
- <dependency>
- <groupId>org.springdoc</groupId>
- <artifactId>springdoc-openapi-ui</artifactId>
- <version>1.6.14</version>
- </dependency>
复制代码
对于Spring Boot 3.x,需要使用SpringDoc 2.x版本:
- <dependency>
- <groupId>org.springdoc</groupId>
- <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
- <version>2.1.0</version>
- </dependency>
复制代码
确认应用程序的主类上有@SpringBootApplication注解。
尝试访问不同的路径:
• SpringDoc 1.x:/swagger-ui.html
• SpringDoc 2.x:/swagger-ui/index.html
2. 控制器方法未显示在Swagger UI中
问题:某些控制器方法没有在Swagger UI中显示。
解决方案:
1. 确认控制器类上有@RestController或@Controller注解。
2. 确认方法上有HTTP映射注解,如@GetMapping、@PostMapping等。
3. 检查是否使用了@Hidden注解,该注解会隐藏API。
4. - 如果使用了Spring Security,确保已配置允许访问Swagger相关的路径:@Configuration
- @EnableWebSecurity
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
- .anyRequest().authenticated();
- }
- }
复制代码
确认控制器类上有@RestController或@Controller注解。
确认方法上有HTTP映射注解,如@GetMapping、@PostMapping等。
检查是否使用了@Hidden注解,该注解会隐藏API。
如果使用了Spring Security,确保已配置允许访问Swagger相关的路径:
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
- .anyRequest().authenticated();
- }
- }
复制代码
3. 请求/响应模型显示不正确
问题:Swagger UI中显示的请求/响应模型与实际不符。
解决方案:
1. 确保模型类上有@Schema注解,并且属性上也有相应的@Schema注解。
2. - 对于泛型类型,使用@ArraySchema注解:@ArraySchema(schema = @Schema(implementation = User.class))
- private List<User> users;
复制代码 3. - 对于复杂的继承关系,可以使用@Schema(allOf = {...})注解:@Schema(allOf = {BaseEntity.class, User.class})
- public class User extends BaseEntity {
- // ...
- }
复制代码 4. 如果使用了@JsonIgnore或@JsonProperty等Jackson注解,确保它们与Swagger注解不冲突。
确保模型类上有@Schema注解,并且属性上也有相应的@Schema注解。
对于泛型类型,使用@ArraySchema注解:
- @ArraySchema(schema = @Schema(implementation = User.class))
- private List<User> users;
复制代码
对于复杂的继承关系,可以使用@Schema(allOf = {...})注解:
- @Schema(allOf = {BaseEntity.class, User.class})
- public class User extends BaseEntity {
- // ...
- }
复制代码
如果使用了@JsonIgnore或@JsonProperty等Jackson注解,确保它们与Swagger注解不冲突。
4. 文件上传API显示不正确
问题:文件上传API在Swagger UI中显示不正确或无法测试。
解决方案:
1. - 确保使用了正确的请求映射和消费类型:@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public ResponseEntity<FileUploadResult> uploadFile(
- @RequestParam("file") MultipartFile file) {
- // ...
- }
复制代码 2. 确保参数类型是MultipartFile,并且使用了@RequestParam注解。
3. - 对于多个文件上传,使用数组或集合类型:@PostMapping(value = "/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public ResponseEntity<List<FileUploadResult>> batchUploadFiles(
- @RequestParam("files") MultipartFile[] files) {
- // ...
- }
复制代码
确保使用了正确的请求映射和消费类型:
- @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public ResponseEntity<FileUploadResult> uploadFile(
- @RequestParam("file") MultipartFile file) {
- // ...
- }
复制代码
确保参数类型是MultipartFile,并且使用了@RequestParam注解。
对于多个文件上传,使用数组或集合类型:
- @PostMapping(value = "/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public ResponseEntity<List<FileUploadResult>> batchUploadFiles(
- @RequestParam("files") MultipartFile[] files) {
- // ...
- }
复制代码
5. 认证信息无法正确传递
问题:在Swagger UI中测试需要认证的API时,认证信息无法正确传递。
解决方案:
1. - 确保已定义安全方案:@SecurityScheme(
- name = "bearerAuth",
- type = SecuritySchemeType.HTTP,
- scheme = "bearer",
- bearerFormat = "JWT"
- )
- @SpringBootApplication
- public class MyApp {
- // ...
- }
复制代码 2. - 在需要认证的API上添加@SecurityRequirement注解:@SecurityRequirement(name = "bearerAuth")
- @GetMapping("/protected")
- public ResponseEntity<String> getProtectedResource() {
- // ...
- }
复制代码 3. 在Swagger UI中,点击右上角的”Authorize”按钮,输入认证信息。
4. - 如果使用的是API密钥认证,确保正确配置了@SecurityScheme:@SecurityScheme(
- name = "apiKey",
- type = SecuritySchemeType.APIKEY,
- in = SecuritySchemeIn.HEADER,
- paramName = "X-API-KEY"
- )
复制代码
确保已定义安全方案:
- @SecurityScheme(
- name = "bearerAuth",
- type = SecuritySchemeType.HTTP,
- scheme = "bearer",
- bearerFormat = "JWT"
- )
- @SpringBootApplication
- public class MyApp {
- // ...
- }
复制代码
在需要认证的API上添加@SecurityRequirement注解:
- @SecurityRequirement(name = "bearerAuth")
- @GetMapping("/protected")
- public ResponseEntity<String> getProtectedResource() {
- // ...
- }
复制代码
在Swagger UI中,点击右上角的”Authorize”按钮,输入认证信息。
如果使用的是API密钥认证,确保正确配置了@SecurityScheme:
- @SecurityScheme(
- name = "apiKey",
- type = SecuritySchemeType.APIKEY,
- in = SecuritySchemeIn.HEADER,
- paramName = "X-API-KEY"
- )
复制代码
总结
Swagger是一个强大的API文档工具,通过注解的方式,我们可以直接在代码中描述API,自动生成交互式文档。本文详细介绍了Swagger的常用注解,包括类级别、方法级别、参数级别和模型级别的注解,并通过实际应用场景展示了如何使用这些注解。
通过合理使用Swagger注解,我们可以:
1. 提高API文档的准确性和可维护性
2. 提供交互式API文档,方便前端开发和测试
3. 减少手动编写文档的工作量
4. 促进团队协作和沟通
在实际项目中,建议遵循以下最佳实践:
1. 为API合理分组,使用@Tag注解
2. 为每个API提供清晰的描述和示例
3. 详细描述请求参数和响应模型
4. 对于需要认证的API,正确配置安全方案
5. 定期更新文档,保持与代码同步
希望本文能帮助你深入理解Swagger注解的使用,提升API开发能力。在实际应用中,不断探索和实践,你会发现Swagger的更多强大功能。 |
|