一、Swagger简介
出现的背景
在现代软件开发中,前后端分离的架构已成为主流。前端(Web、App)和后端(服务器)通过一套API进行通信。随着项目规模越来越大,API的数量也急剧增加,带来了一系列问题:
- 沟通成本高:前端开发人员需要知道如何调用API,包括URL是什么、需要传什么参数、返回什么数据。后端开发人员通常通过编写Word或Markdown文档来告知前端,但这种方式效率低下且容易出错。
- 文档维护难:代码在不断迭代,API也随之变化。如果API变更了,而文档没有及时更新,就会导致前后端联调时出现各种问题,浪费大量时间。
- 测试不方便:开发人员需要一个简单快捷的方式来测试API接口,确保其功能符合预期。传统的测试方式通常需要借助Postman等第三方工具,操作相对繁琐。
Swagger,解决什么问题?
Swagger(现在其规范被称为 OpenAPI Specification )正是为了解决上述问题而诞生的。它是一套围绕OpenAPI规范构建的开源工具框架,可以帮助你 设计、构建、记录和使用 RESTful API。
简单来说,Swagger的核心价值在于:
- 自动生成交互式文档:只需要在代码中添加一些注解,Swagger就能自动生成一份美观、详细且可交互的API文档。前端开发者可以直接在文档页面上查看API详情并进行调用测试。
- 保证文档与代码同步:文档是根据代码生成的,所以只要代码更新了,文档就会自动更新,彻底解决了文档与代码不一致的问题。
- 标准化API定义:它提供了一套标准的、与语言无关的规范来描述API,使得计算机和人都能轻松理解API的功能和期望。
- 简化团队协作:通过这份标准的、可交互的文档,前端、后端和测试人员可以高效地协作,减少沟通障碍。
- 多语言支持:支持多种编程语言和框架
简单来说,Swagger就是让你的API文档"活"起来,永远与代码保持一致,并且可以直接在浏览器中测试。
二、Swagger2和Swagger3区别
我们通常说的Swagger 3,实际上是指实现了 OpenAPI 3.0 (OAS 3) 规范的工具。而Swagger 2对应的是旧的 Swagger 2.0 规范。OAS 3是一个重大的升级,带来了许多改进。
在Spring Boot生态中,最直观的体现是依赖库的变化:
- Swagger 2 : 主要使用
SpringFox库。 - Swagger 3 (OAS 3) : 主流使用
SpringDoc库,它与Spring Boot 3.x及更高版本兼容性更好,配置也更简单。
主要区别对比:
| 特性 | Swagger 2 (SpringFox) | Swagger 3 / OpenAPI 3 (SpringDoc) | 优势说明 |
|---|---|---|---|
| 规范 | Swagger 2.0 | OpenAPI 3.0 | OAS 3是当前行业标准,功能更强大。 |
| 依赖库 | springfox-swagger2 |
springdoc-openapi-starter-webmvc-ui |
SpringDoc 对新版Spring Boot支持更好,配置更自动化。 |
| 组件化 | 定义有限,复用性一般 | 引入 components 对象,可轻松复用Schemas, Parameters, Responses等 |
结构更清晰,提高了规范的可维护性。 |
| 服务器定义 | 只能定义单个 host 和 basePath |
可定义多个 servers,每个server可以有自己的URL和描述 |
更灵活,轻松应对开发、测试、生产等多个环境。 |
| 请求体 | 使用 in: body 的参数来描述 |
引入独立的 requestBody 对象,可以描述更复杂的请求体和多种媒体类型 |
对请求体的描述更精确、更强大。 |
| 安全性 | securityDefinitions |
securitySchemes |
增强了对OAuth2和OpenID Connect的支持,描述更灵活。 |
结论: 如果你正在开始一个新项目,或者你的项目使用的是Spring Boot 2.x.x后期版本或3.x.x,强烈建议直接使用基于OpenAPI 3的SpringDoc。
三、集成 Swagger 3 (SpringDoc)
我们以目前最主流的SpringDoc为例,介绍如何在Spring Boot项目中集成。
1、如何集成?
集成过程非常简单,只需要一步:在你的pom.xml文件中添加依赖。
xml
<!-- 访问 /swagger-ui.html 即可查看API文档 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version> <!-- 建议使用最新稳定版 -->
</dependency>
2、最小配置是什么样的?
答案是:不需要任何Java配置!一副牛B哄哄的样子
是的,你没有看错。只要你将上面的依赖添加到项目中,并重新启动你的Spring Boot应用,SpringDoc就会自动开始工作。它会:
- 扫描项目中所有被
@RestController注解的类。 - 解析被
@RequestMapping,@GetMapping,@PostMapping等注解标记的方法,生成API信息。
启动成功后,在浏览器中访问 http://localhost:8080/swagger-ui.html (端口号替换成你自己的),你就可以看到自动生成的API文档了。
访问地址:
- Swagger UI界面 :
http://localhost:8080/swagger-ui.html - API文档JSON :
http://localhost:8080/v3/api-docs
四、配置 Swagger 3
虽然零配置就能运行,但在实际项目中,我们通常需要自定义一些信息,如作者、标题、版本等,让文档更清晰。springdoc-openapi 同时支持 YML 属性配置 和 Java 配置类 两种方式。它们各有优势,并且可以同时使用,相辅相成。
配置全局信息(标题、作者等)
这些信息可以通过项目的配置文件 application.properties 或 application.yml 来设置。这种方式简单直观,适用于大多数情况。
使用 application.yml (推荐):
yml
springdoc:
api-docs:
info:
title: '我的项目API文档' # API文档标题,显示在Swagger UI顶部
description: '这是一个使用Spring Boot和Swagger 3构建的示例项目。' # API详细描述,支持Markdown格式
version: 'v1.0.0' # API版本号
termsOfService: 'http://swagger.io/terms/' # 服务条款URL,用户需要遵守的条款
# 联系信息
contact:
name: '洛小豆' # 负责人或团队名称
email: 'luoxiaodou@juejin.com' # 作者邮箱
url: 'https://juejin.cn/user/2049145406229127' # 联系网址或个人网站
# 许可证信息
license:
name: 'Apache 2.0' # 使用的许可证名称(如:Apache 2.0, MIT等)
url: 'https://www.apache.org/licenses/LICENSE-2.0.html' # 许可证详细内容的URL
使用 Java 配置类
如果希望通过编程方式来配置,可以使用一个 Java 配置类。这种方式提供了更大的灵活性,例如,你可以根据环境动态地设置不同的元数据。
你需要创建一个配置类,并使用 @Configuration 和 @Bean 注解来定义 OpenAPI 对象。
java
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;
/**
* OpenAPI 配置类
* 用于配置Swagger/OpenAPI文档生成
*/
public class OpenAPIConfig {
/**
* 创建自定义OpenAPI配置Bean
* 该方法会返回一个配置好的OpenAPI实例,用于生成API文档
*
* @return OpenAPI 配置实例
*/
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
// 设置API基本信息
.info(new Info()
// API文档标题,显示在Swagger UI顶部
.title("我的项目API文档")
// API详细描述,支持Markdown格式
.description("这是一个使用Spring Boot和Swagger 3构建的示例项目。")
// API版本号
.version("v1.0.0")
// 服务条款URL
.termsOfService("http://swagger.io/terms/")
// 设置联系信息
.contact(new Contact()
// 负责人或团队名称
.name("洛小豆")
// 个人主页或项目文档链接
.url("https://juejin.cn/user/2049145406229127")
// 联系邮箱
.email("luoxiaodou@juejin.com"))
// 设置许可证信息
.license(new License()
// 许可证名称
.name("Apache 2.0")
// 许可证详细内容URL
.url("http://www.apache.org/licenses/LICENSE-2.0.html"))
);
}
}
配置API扫描范围
SpringDoc 提供了多种方式来精确控制哪些API需要被展示在文档中。
YML 属性配置
可以通过 springdoc.packagesToScan 和 springdoc.pathsToMatch 属性来配置扫描范围。
packagesToScan: 指定需要扫描的 Java 包名。pathsToMatch: 指定需要匹配的 URL 路径模式。packagesToExclude: 定义排除的包pathsToExclude: 定义排除的路径
yaml
springdoc:
# 扫描指定包下的控制器
packagesToScan: cn.luoxiaodou.controller
# 或者更精确地指定多个包
# packagesToScan: com.yourcompany.api, com.yourcompany.service.controller
# 匹配指定的 URL 路径
pathsToMatch: /api/v1/**
# 或者匹配多个路径
# pathsToMatch: /public/**, /private/**
# 定义排除的包和路径
# 即使某个类在 packagesToScan 范围内,如果它在 com.example.controller.internal 包下,也会被排除
packagesToExclude: cn.luoxiaodou.controller.internal
# 排除所有以 /public/health 开头的路径
pathsToExclude: /public/health/**
Java 配置类
需要更精细、更灵活的控制,比如为不同的模块或版本创建独立的 API 文档,或者需要根据业务逻辑动态生成配置,那么 Java 配置类是更好的选择。
java
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
// 全局配置:定义 API 的基本信息
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("我的应用 API 文档")
.description("这是一个通过 Java 配置类演示 springdoc-openapi3 的 API 文档。")
.version("v1.0.0"));
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public-apis") // 分组名称
// pathsToMatch(String... pathsToMatch),这里可以输入多个值
.pathsToMatch("/public/**") // 匹配所有以 /public/ 开头的路径
.pathsToExclude("/public/health/**") // 排除健康检查路径
.build();
}
}
配置API的分组
当项目很大,API很多时,把所有API都放在一个页面上会显得很杂乱。分组功能可以让你根据模块(如用户模块、订单模块)来组织API。
这需要通过Java代码配置。创建一个配置类,并定义多个 GroupedOpenApi 类型的Bean。
java
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public GroupedOpenApi userApi() {
return GroupedOpenApi.builder()
.group("用户模块") // 分组名
.pathsToMatch("/users/**") // 该组包含的API路径
.build();
}
@Bean
public GroupedOpenApi orderApi() {
return GroupedOpenApi.builder()
.group("订单模块")
.packagesToScan("cn.luoxiaodou.project.controller.order") // 该组包含的包
.build();
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("公共接口")
.pathsToMatch("/**")
.pathsToExclude("/users/**", "/orders/**") // 排除其他组已经包含的路径
.build();
}
}
配置完成后,Swagger UI页面的右上角会出现一个下拉框,你可以在不同的分组之间切换。
为什么不推荐使用 YML 配置进行分组?
虽然你可以通过 YML 配置来扫描和排除路径,但这只能实现单个、全局的配置,无法像 GroupedOpenApi 那样创建多个独立的、可切换的分组。如果你只在乎扫描范围,YML 足够了;但如果你的目标是更好地组织和呈现文档,Java 配置类是实现分组的唯一推荐方式。
多环境配置,启用/禁用Swagger
在使用 springdoc-openapi3 时,有时你需要根据不同的环境(例如开发、测试或生产环境)来启用或禁用 Swagger 文档。
YML 属性配置
这是最常用、最简单的配置方式。你可以通过设置 springdoc.swagger-ui.enabled 属性来控制 Swagger UI 的启用或禁用。
yaml
# 在开发和测试环境中启用 Swagger UI
springdoc:
swagger-ui:
enabled: true
api-docs:
enabled: true
# 在生产环境中禁用 Swagger UI
springdoc:
swagger-ui:
enabled: false
api-docs:
enabled: false
通常,我们会将这个配置放在不同的配置文件中,例如 application-dev.yml(启用)和 application-prod.yml(禁用),这样 Spring Boot 会根据你的活动 profile 自动加载对应的配置。
Java 配置类
如果你需要更复杂的逻辑来决定是否启用 Swagger,比如根据某些自定义条件,那么可以使用 Java 配置类。
你可以创建一个配置类,并利用 @ConditionalOnProperty 注解来实现条件启用。
java
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name = "springdoc.swagger-ui.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerEnableConfig {
// 这个配置类只有在 springdoc.swagger-ui.enabled=true 时才会被加载
// matchIfMissing = true 意味着如果该属性不存在,则默认启用
}
实体类(Model)配置
Swagger会自动扫描你在API中用作参数(@RequestBody)或返回值(ResponseEntity)的Java实体类,并在文档的 "Schemas" 部分展示其结构。
我们可以通过注解来丰富实体类的信息。
-
@Schema: 这是最核心的注解,用于描述一个实体类或其字段。description: 描述信息。example: 示例值。requiredMode: 字段是否必须。RequiredMode.REQUIRED(必须),RequiredMode.NOT_REQUIRED(非必须)。defaultValue: 默认值。
@Schema注解详细使用代码
java
@Schema(
description = "用户详细信息", // 描述
example = "User object example", // 示例说明
title = "User", // 标题
requiredProperties = {"username", "email"} // 必填字段
)
public class UserDetail {
@Schema(
description = "用户唯一标识", // 字段描述
example = "1001", // 示例值
required = true, // 是否必填
minimum = "1", // 最小值
maximum = "999999" // 最大值
)
private Long id;
@Schema(
description = "用户名",
example = "zhangsan",
required = true,
minLength = 3, // 最小长度
maxLength = 20, // 最大长度
pattern = "^[a-zA-Z0-9_]+$" // 正则表达式
)
private String username;
@Schema(
description = "用户类型",
example = "NORMAL",
allowableValues = {"NORMAL", "VIP", "ADMIN"} // 允许的值
)
private UserType userType;
@Schema(description = "敏感信息", hidden = true) // 隐藏字段
private String password;
@Schema(
description = "用户标签",
example = "["新用户", "活跃用户"]"
)
private List<String> tags;
}
验证注解结合使用
java
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 UserRegisterRequest {
@Schema(description = "用户名", example = "zhangsan")
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@Schema(description = "密码", example = "123456")
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度至少6位")
private String password;
@Schema(description = "邮箱", example = "zhangsan@example.com")
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@Schema(description = "年龄", example = "25")
@Min(value = 0, message = "年龄不能小于0")
@Max(value = 150, message = "年龄不能大于150")
private Integer age;
}
注意 :SpringDoc 会自动识别 jakarta.validation.constraints (或 javax.validation.constraints) 包下的注解,如 @NotBlank, @NotNull, @Size 等,并自动将字段标记为必填项和添加校验规则说明。
五、注解汇总
控制器类注解:
| 注解 | 作用 | 常用属性 | 示例 |
|---|---|---|---|
@Tag |
给控制器添加标签和描述 | name, description | 标识API分组 |
@Hidden |
隐藏整个控制器 | - | 不在文档中显示 |
使用示例:
java
@Tag(name = "用户管理", description = "用户相关的增删改查操作")
@RestController
@RequestMapping("/api/user")
public class UserController {
// 控制器方法
}
方法级注解
| 注解 | 作用 | 常用属性 | 示例 |
|---|---|---|---|
@Operation |
描述API操作 | summary, description, tags | 方法说明 |
@Parameter |
描述参数 | name, description, required, example | 参数说明 |
@Parameters |
多个参数描述 | value | 包含多个@Parameter |
@ApiResponse |
描述响应 | responseCode, description | 响应说明 |
@ApiResponses |
多个响应描述 | value | 包含多个@ApiResponse |
@Hidden |
隐藏方法 | - | 不在文档中显示 |
详细使用示例
java
@Tag(name = "用户管理", description = "用户相关操作")
@RestController
@RequestMapping("/api/user")
public class UserController {
@Operation(
summary = "获取用户列表",
description = "分页获取系统中所有用户的信息,支持按条件筛选",
tags = {"用户查询"}
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "获取成功"),
@ApiResponse(responseCode = "400", description = "请求参数错误"),
@ApiResponse(responseCode = "500", description = "服务器内部错误")
})
@GetMapping("/list")
public PageResult<User> getUserList(
@Parameter(description = "页码", example = "1", required = true)
@RequestParam(defaultValue = "1") Integer page,
@Parameter(description = "每页大小", example = "10", required = true)
@RequestParam(defaultValue = "10") Integer size,
@Parameter(description = "用户名搜索关键词", example = "zhang")
@RequestParam(required = false) String keyword
) {
// 业务逻辑
return userService.findUsers(page, size, keyword);
}
@Operation(summary = "创建用户", description = "创建新的用户账户")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "创建成功"),
@ApiResponse(responseCode = "400", description = "用户信息验证失败"),
@ApiResponse(responseCode = "409", description = "用户名或邮箱已存在")
})
@PostMapping("/create")
public User createUser(
@Parameter(description = "用户信息", required = true)
@Valid @RequestBody UserCreateRequest request
) {
return userService.createUser(request);
}
@Operation(summary = "更新用户信息")
@PutMapping("/{id}")
public User updateUser(
@Parameter(description = "用户ID", example = "1001", required = true)
@PathVariable Long id,
@Parameter(description = "更新的用户信息", required = true)
@Valid @RequestBody UserUpdateRequest request
) {
return userService.updateUser(id, request);
}
@Operation(summary = "删除用户")
@DeleteMapping("/{id}")
public void deleteUser(
@Parameter(description = "要删除的用户ID", example = "1001", required = true)
@PathVariable Long id
) {
userService.deleteUser(id);
}
@Hidden // 隐藏此接口,不在文档中显示
@GetMapping("/internal")
public String internalApi() {
return "内部接口";
}
}
实体类注解(重要):
| 注解 | 作用 | 常用属性 | 示例 |
|---|---|---|---|
@Schema |
描述实体类或字段 | description, example, required | 实体说明 |
@JsonIgnore |
忽略字段 | - | 隐藏敏感信息 |
@JsonProperty |
自定义字段名 | value | 字段重命名 |
java
// 请求实体
@Schema(description = "用户创建请求")
public class UserCreateRequest {
@Schema(description = "用户名", example = "zhangsan", required = true)
@NotBlank(message = "用户名不能为空")
private String username;
@Schema(description = "邮箱", example = "zhangsan@example.com", required = true)
@Email(message = "邮箱格式不正确")
private String email;
@Schema(description = "年龄", example = "25")
@Min(0) @Max(150)
private Integer age;
}
// 响应实体
@Schema(description = "分页结果")
public class PageResult<T> {
@Schema(description = "数据列表")
private List<T> data;
@Schema(description = "总记录数", example = "100")
private Long total;
@Schema(description = "当前页码", example = "1")
private Integer page;
@Schema(description = "每页大小", example = "10")
private Integer size;
}
wagger2兼容注解
Swagger3仍然支持Swagger2的注解,但建议使用新的OpenAPI 3注解:
| Swagger2注解 | Swagger3替代注解 | 说明 |
|---|---|---|
@Api |
@Tag |
控制器描述 |
@ApiOperation |
@Operation |
方法描述 |
@ApiParam |
@Parameter |
参数描述 |
@ApiModel |
@Schema |
实体类描述 |
@ApiModelProperty |
@Schema |
实体字段描述 |
@ApiResponse |
@ApiResponse |
响应描述(相同) |
@ApiIgnore |
@Hidden |
隐藏接口 |
完整的最佳实践示例
java
/**
* 商品管理控制器
*/
@Tag(name = "商品管理", description = "商品的增删改查操作")
@RestController
@RequestMapping("/api/product")
@Validated
public class ProductController {
@Autowired
private ProductService productService;
@Operation(
summary = "获取商品详情",
description = "根据商品ID获取商品的详细信息,包括库存、价格等"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "获取成功"),
@ApiResponse(responseCode = "404", description = "商品不存在"),
@ApiResponse(responseCode = "500", description = "服务器错误")
})
@GetMapping("/{id}")
public Product getProductById(
@Parameter(description = "商品ID", example = "1001", required = true)
@PathVariable Long id
) {
return productService.findById(id);
}
@Operation(
summary = "搜索商品",
description = "根据关键词、分类、价格区间等条件搜索商品"
)
@GetMapping("/search")
public PageResult<Product> searchProducts(
@Parameter(description = "搜索关键词", example = "手机")
@RequestParam(required = false) String keyword,
@Parameter(description = "商品分类ID", example = "1")
@RequestParam(required = false) Long categoryId,
@Parameter(description = "最低价格", example = "100.00")
@RequestParam(required = false) BigDecimal minPrice,
@Parameter(description = "最高价格", example = "5000.00")
@RequestParam(required = false) BigDecimal maxPrice,
@Parameter(description = "页码", example = "1")
@RequestParam(defaultValue = "1") @Min(1) Integer page,
@Parameter(description = "每页大小", example = "20")
@RequestParam(defaultValue = "20") @Min(1) @Max(100) Integer size
) {
return productService.searchProducts(keyword, categoryId, minPrice, maxPrice, page, size);
}
@Operation(
summary = "创建商品",
description = "创建新的商品信息,需要管理员权限"
)
@ApiResponses({
@ApiResponse(responseCode = "201", description = "创建成功"),
@ApiResponse(responseCode = "400", description = "商品信息验证失败"),
@ApiResponse(responseCode = "403", description = "权限不足")
})
@PostMapping
public Product createProduct(
@Parameter(description = "商品信息", required = true)
@Valid @RequestBody ProductCreateRequest request
) {
return productService.createProduct(request);
}
@Operation(summary = "批量上传商品图片")
@PostMapping("/{id}/images")
public List<String> uploadProductImages(
@Parameter(description = "商品ID", required = true)
@PathVariable Long id,
@Parameter(description = "商品图片文件", required = true)
@RequestParam("files") MultipartFile[] files
) {
return productService.uploadImages(id, files);
}
@Hidden // 管理员内部接口,不对外显示
@DeleteMapping("/batch")
public void batchDeleteProducts(@RequestBody List<Long> ids) {
productService.batchDelete(ids);
}
}
/**
* 商品信息实体类
*/
@Schema(description = "商品信息")
public class Product {
@Schema(description = "商品ID", example = "1001")
private Long id;
@Schema(description = "商品名称", example = "iPhone 15 Pro", required = true)
private String name;
@Schema(description = "商品描述", example = "最新款iPhone,配备A17芯片")
private String description;
@Schema(description = "商品价格", example = "7999.00", required = true)
private BigDecimal price;
@Schema(description = "库存数量", example = "100")
private Integer stock;
@Schema(description = "商品分类ID", example = "1")
private Long categoryId;
@Schema(description = "商品分类名称", example = "智能手机")
private String categoryName;
@Schema(description = "商品图片URL列表")
private List<String> imageUrls;
@Schema(description = "商品状态", example = "ACTIVE",
allowableValues = {"ACTIVE", "INACTIVE", "SOLD_OUT"})
private ProductStatus status;
@Schema(description = "商品标签", example = "["热销", "新品"]")
private List<String> tags;
@Schema(description = "创建时间", example = "2023-12-01T10:30:00")
private LocalDateTime createTime;
@Schema(description = "更新时间", example = "2023-12-01T10:30:00")
private LocalDateTime updateTime;
@JsonIgnore // 不在API文档中显示
private String internalCode;
// getter和setter方法省略...
}
/**
* 商品创建请求
*/
@Schema(description = "商品创建请求")
public class ProductCreateRequest {
@Schema(description = "商品名称", example = "iPhone 15 Pro", required = true)
@NotBlank(message = "商品名称不能为空")
@Size(max = 100, message = "商品名称长度不能超过100字符")
private String name;
@Schema(description = "商品描述", example = "最新款iPhone")
@Size(max = 1000, message = "商品描述长度不能超过1000字符")
private String description;
@Schema(description = "商品价格", example = "7999.00", required = true)
@NotNull(message = "商品价格不能为空")
@DecimalMin(value = "0.01", message = "商品价格必须大于0")
@Digits(integer = 10, fraction = 2, message = "价格格式不正确")
private BigDecimal price;
@Schema(description = "库存数量", example = "100", required = true)
@NotNull(message = "库存数量不能为空")
@Min(value = 0, message = "库存数量不能小于0")
private Integer stock;
@Schema(description = "商品分类ID", example = "1", required = true)
@NotNull(message = "商品分类不能为空")
private Long categoryId;
@Schema(description = "商品标签", example = "["热销", "新品"]")
private List<String> tags;
}
六、常见问题和解决方案
问题1:中文乱码
yml
# application.yml
server:
servlet:
encoding:
charset: UTF-8
force: true
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
# 解决中文显示问题
config-url: /v3/api-docs/swagger-config
问题2:接口返回类型显示不正确
java
// 错误方式:返回类型模糊
@GetMapping("/users")
public Object getUsers() { ... }
// 正确方式:明确返回类型
@GetMapping("/users")
public ApiResult<List<User>> getUsers() { ... }
// 或者使用@Operation注解明确指定
@Operation(summary = "获取用户列表")
@ApiResponse(responseCode = "200",
description = "成功",
content = @Content(schema = @Schema(implementation = User.class)))
@GetMapping("/users")
public Object getUsers() { ... }
问题3:枚举类型显示优化
java
@Schema(description = "用户状态")
public enum UserStatus {
@Schema(description = "正常")
ACTIVE("1", "正常"),
@Schema(description = "禁用")
INACTIVE("0", "禁用"),
@Schema(description = "锁定")
LOCKED("2", "锁定");
private String code;
private String description;
UserStatus(String code, String description) {
this.code = code;
this.description = description;
}
// getter方法...
}