Swagger 与 OpenAPI

Spring Boot 3.x 推荐使用 springdoc-openapi(兼容 OpenAPI 3.0)生成 API 文档,替代已停止维护的 Springfox。访问 http://localhost:8080/swagger-ui.html 可查看交互式文档。


依赖

<!-- Spring Boot 3.x 对应 springdoc 2.x -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.5.0</version>
</dependency>

WebFlux 项目替换为 springdoc-openapi-starter-webflux-ui


快速开始

依赖加入后无需任何配置,访问以下地址:

地址说明
/swagger-ui.html交互式 UI(重定向到 /swagger-ui/index.html
/v3/api-docsOpenAPI JSON 规范
/v3/api-docs.yamlOpenAPI YAML 规范

全局 API 信息

@Configuration
public class OpenApiConfig {
 
    @Bean
    public OpenAPI openAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("订单服务 API")
                .description("订单管理系统 REST 接口文档")
                .version("v1.0.0")
                .contact(new Contact()
                    .name("后端团队")
                    .email("backend@example.com"))
                .license(new License()
                    .name("Apache 2.0")
                    .url("https://www.apache.org/licenses/LICENSE-2.0"))
            )
            .servers(List.of(
                new Server().url("https://api.example.com").description("生产环境"),
                new Server().url("http://localhost:8080").description("本地开发")
            ));
    }
}

接口注解

@Tag — 接口分组

@RestController
@RequestMapping("/api/v1/orders")
@Tag(name = "订单管理", description = "创建、查询、更新、删除订单")
public class OrderController { ... }

@Operation — 方法说明

@GetMapping("/{id}")
@Operation(
    summary = "根据 ID 查询订单",
    description = "返回单个订单的完整信息,包含商品明细",
    operationId = "getOrderById"
)
public ResponseEntity<OrderDto> getOrder(@PathVariable Long id) { ... }

@Parameter — 参数说明

@GetMapping
@Operation(summary = "分页查询订单列表")
public Page<OrderDto> listOrders(
        @Parameter(description = "用户 ID", required = true, example = "1001")
        @RequestParam Long userId,
 
        @Parameter(description = "订单状态", schema = @Schema(
            allowableValues = {"PENDING", "PAID", "SHIPPED", "DONE"}))
        @RequestParam(required = false) String status,
 
        @Parameter(hidden = true)   // 隐藏 Pageable 内部参数
        Pageable pageable) { ... }

@ApiResponse — 响应说明

@PostMapping
@Operation(summary = "创建订单")
@ApiResponses({
    @ApiResponse(responseCode = "201", description = "创建成功",
        content = @Content(schema = @Schema(implementation = OrderDto.class))),
    @ApiResponse(responseCode = "400", description = "请求参数校验失败",
        content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
    @ApiResponse(responseCode = "401", description = "未认证",
        content = @Content),
    @ApiResponse(responseCode = "500", description = "服务器内部错误",
        content = @Content)
})
public ResponseEntity<OrderDto> createOrder(
        @Valid @RequestBody CreateOrderRequest request) { ... }

@Schema — DTO 字段说明

@Schema(description = "创建订单请求")
public record CreateOrderRequest(
 
    @Schema(description = "用户 ID", example = "1001", requiredMode = REQUIRED)
    @NotNull Long userId,
 
    @Schema(description = "收货地址", example = "北京市朝阳区XX路1号", requiredMode = REQUIRED)
    @NotBlank String address,
 
    @Schema(description = "商品列表,至少一项", requiredMode = REQUIRED)
    @NotEmpty List<OrderItemRequest> items,
 
    @Schema(description = "备注", example = "请尽快发货", requiredMode = NOT_REQUIRED)
    String remark
) {}

安全认证配置(Bearer Token)

@Bean
public OpenAPI openAPI() {
    SecurityScheme bearerScheme = new SecurityScheme()
        .type(SecurityScheme.Type.HTTP)
        .scheme("bearer")
        .bearerFormat("JWT")
        .name("bearerAuth");
 
    SecurityRequirement securityRequirement =
        new SecurityRequirement().addList("bearerAuth");
 
    return new OpenAPI()
        .info(new Info().title("API 文档").version("1.0"))
        .addSecurityItem(securityRequirement)           // 全局应用认证
        .components(new Components()
            .addSecuritySchemes("bearerAuth", bearerScheme));
}

部分接口无需认证时,在方法上覆盖:

@PostMapping("/login")
@Operation(
    summary = "用户登录",
    security = {}    // 空数组表示此接口不需要认证
)
public TokenResponse login(@RequestBody LoginRequest req) { ... }

JWT 认证方案详见 OAuth2与JWT


接口分组

将不同模块的接口分离到独立的文档组:

@Bean
public GroupedOpenApi orderApi() {
    return GroupedOpenApi.builder()
        .group("订单模块")
        .pathsToMatch("/api/v1/orders/**")
        .build();
}
 
@Bean
public GroupedOpenApi userApi() {
    return GroupedOpenApi.builder()
        .group("用户模块")
        .pathsToMatch("/api/v1/users/**")
        .build();
}
 
@Bean
public GroupedOpenApi adminApi() {
    return GroupedOpenApi.builder()
        .group("管理后台")
        .pathsToMatch("/admin/**")
        .addOpenApiCustomizer(openApi ->
            openApi.info(new Info().title("管理后台 API").version("1.0")))
        .build();
}

配置项

springdoc:
  # Swagger UI 路径
  swagger-ui:
    path: /swagger-ui.html
    tags-sorter: alpha          # 按 tag 名字母排序
    operations-sorter: method   # 按 HTTP 方法排序
    try-it-out-enabled: true    # 默认展开"Try it out"
    filter: true                # 显示接口搜索框
  # API 文档路径
  api-docs:
    path: /v3/api-docs
    enabled: true
  # 显示 Actuator 端点(默认不显示)
  show-actuator: false
  # 包扫描范围
  packages-to-scan: com.example.controller
  # 生产环境禁用
  # api-docs.enabled: false

生产环境关闭文档:

# application-prod.yml
springdoc:
  api-docs:
    enabled: false
  swagger-ui:
    enabled: false

统一响应包装

统一响应格式 配合,让 Swagger 正确展示包装后的响应类型:

// 泛型包装类
@Schema(description = "统一响应")
public record Result<T>(
    @Schema(description = "是否成功") boolean success,
    @Schema(description = "响应数据") T data,
    @Schema(description = "错误信息") String message
) {}
 
// 在 @ApiResponse 中指定包装类型
@ApiResponse(responseCode = "200",
    content = @Content(schema = @Schema(implementation = OrderResultSchema.class)))
// 辅助 Schema 类(解决泛型擦除问题)
class OrderResultSchema extends Result<OrderDto> {}

忽略特定接口

// 方法级:隐藏单个接口
@Hidden
@GetMapping("/internal/health")
public String health() { return "ok"; }
 
// 类级:隐藏整个 Controller
@Hidden
@RestController
public class InternalController { ... }

导出为静态文档

CI 阶段可以将 /v3/api-docs 导出为 JSON 文件,再用 Redoc / Slate 渲染:

# 应用启动后导出
curl http://localhost:8080/v3/api-docs -o api-docs.json
 
# 用 redocly 生成静态 HTML
npx @redocly/cli build-docs api-docs.json -o api-docs.html

相关链接