為何選擇 Swagger (OpenAPI 3)
- 代碼即文檔:註解維護在 Controller/Model 中,避免文檔滯後。
- 互動式測試:Swagger UI 直接發送請求、查看響應,前後端並行開發利器。
- 標準化:OpenAPI 3 規範,工具生態豐富(代碼生成、Mock Server、測試框架整合)。
- 輕量級:無需額外 Wiki/Confluence,適合敏捷小團隊。
Maven 依賴 (Spring Boot 2.x + Springfox 3 / Springdoc OpenAPI)
推薦:Springdoc OpenAPI (維護中、支援 Spring Boot 3、OpenAPI 3 原生)
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
<!-- 舊版 Springfox (停止維護) -->
<!--
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
-->
核心配置類
package com.example.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("訂單管理系統 API")
.version("v1.2.0")
.description("提供訂單、商品、用戶核心業務介面")
.contact(new Contact()
.name("API 團隊")
.email("api@example.com")
.url("https://example.com")))
.addServersItem(new Server()
.url("https://api.example.com")
.description("生產環境"))
.addServersItem(new Server()
.url("https://localhost:8080")
.description("本地開發"));
}
}
常用註解速查
| 註解 | 作用域 | 說明 |
@Tag(name="訂單", description="訂單管理") | Controller 類 | 分組標籤,替代舊 @Api |
@Operation(summary="建立訂單", description="...") | 方法 | API 摘要與詳細說明 |
@Parameter(name="id", description="訂單ID", required=true, in=ParameterIn.PATH) | 方法參數 | 參數元數據,替代 @ApiParam |
@Schema(description="訂單實體", example="{\"id\":1}") | Model 類/欄位 | 模型描述,替代 @ApiModel |
@ApiResponses({@ApiResponse(responseCode="200", description="成功"), @ApiResponse(responseCode="400", description="參數錯誤")}) | 方法 | 響應碼文檔 |
@Hidden | 類/方法/欄位 | 隱藏不對外公開的 API |
Controller 實戰範例
@RestController
@RequestMapping("/api/v1/orders")
@Tag(name = "訂單管理", description = "訂單建立、查詢、取消、退款")
public class OrderController {
@PostMapping
@Operation(summary = "建立訂單", description = "根據購物車內容建立新訂單")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "建立成功",
content = @Content(schema = @Schema(implementation = OrderResponse.class))),
@ApiResponse(responseCode = "400", description = "參數驗證失敗",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
public ResponseEntity<OrderResponse> createOrder(
@Valid @RequestBody OrderCreateRequest request,
@Parameter(hidden = true) @CurrentUser UserPrincipal user) {
OrderResponse response = orderService.create(request, user.getId());
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
@GetMapping("/{id}")
@Operation(summary = "查詢訂單詳情")
public ResponseEntity<OrderDetailResponse> getOrder(
@Parameter(description = "訂單 ID", required = true, example = "1001") @PathVariable Long id) {
return ResponseEntity.ok(orderService.getDetail(id));
}
}
Model 類文檔化
@Schema(name = "OrderCreateRequest", description = "建立訂單請求參數")
public class OrderCreateRequest {
@NotNull @Schema(description = "商品 ID", example = "100")
private Long productId;
@Min(1) @Max(999) @Schema(description = "購買數量", example = "2")
private Integer quantity;
@Schema(description = "優惠碼", example = "SUMMER20")
private String couponCode;
// getter/setter
}
@Schema(name = "OrderResponse", description = "訂單響應")
public class OrderResponse {
@Schema(description = "訂單編號", example = "ORD202401010001")
private String orderNo;
@Schema(description = "訂單狀態", allowableValues = {"PENDING", "PAID", "SHIPPED", "CANCELLED"})
private String status;
// getter/setter
}
訪問文檔 UI
- Swagger UI:
https://localhost:8080/swagger-ui.html (Springfox) 或 https://localhost:8080/swagger-ui/index.html (Springdoc)
- OpenAPI JSON:
https://localhost:8080/v3/api-docs
- OpenAPI YAML:
https://localhost:8080/v3/api-docs.yaml
進階功能
1. 分組多模組專案
@Bean
public GroupedOpenApi orderApi() {
return GroupedOpenApi.builder()
.group("order")
.pathsToMatch("/api/v1/orders/**")
.build();
}
@Bean
public GroupedOpenApi productApi() {
return GroupedOpenApi.builder()
.group("product")
.pathsToMatch("/api/v1/products/**")
.build();
}
2. 整合 Spring Security / JWT
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth", new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("輸入 JWT Token")));
}
3. 離線文檔生成 (Maven 插件)
<plugin>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-maven-plugin</artifactId>
<version>2.2.0</version>
<executions>
<execution>
<goals><goal>generate</goal></goals>
</execution>
</executions>
<configuration>
<outputFileName>openapi.json</outputFileName>
<outputDir>${project.build.directory}/docs</outputDir>
</configuration>
</plugin>
遷移指南:Springfox → Springdoc
| Springfox (舊) | Springdoc (新) |
@Api | @Tag |
@ApiOperation | @Operation |
@ApiParam | @Parameter |
@ApiModel | @Schema |
@ApiResponse | @ApiResponse (swagger v3 套件) |
| Docket Bean | GroupedOpenApi Bean |
最佳實踐清單
- 所有對外 API 必須 加上
@Operation(summary=...)。
- 請求/響應 DTO 全部 加上
@Schema 與欄位 example。
- 枚舉欄位使用
allowableValues 列出可選值。
- 內部/管理端 API 加
@Hidden 或配置 GroupedOpenApi 隔離。
- CI/CD 流水線執行
mvn springdoc:generate 產出靜態文檔發布至內部門戶。
- 定期用
swagger-codegen 或 openapi-generator 為前端/第三方生成 SDK。