Spring REST API & Data JPA
18/46
Bài 18 / 46~12 phútError, Validation & API DocsLộ trình · chặng 15/21Miễn phí… lượt xem

OpenAPI và springdoc: Tự Động Sinh Tài Liệu REST API

Tìm hiểu chuẩn OpenAPI 3.1, cơ chế springdoc-openapi tự động quét Controller sinh Swagger UI và kỹ thuật bổ sung metadata với @Operation, @Schema.

TL;DR: OpenAPI Specification 3.1 (OAS) là chuẩn định dạng máy đọc được (JSON/YAML) mô tả toàn bộ hợp đồng tương tác của REST API. Thư viện springdoc-openapi tự động quét các annotation của @RestController lúc khởi động ứng dụng và dựng đối tượng OpenAPI ngay trong bộ nhớ RAM, phục vụ qua /v3/api-docs và giao diện tương tác Swagger UI tại /swagger-ui.html. Cách tiếp cận code-first này biến mã nguồn thành nguồn sự thật duy nhất (Single Source of Truth), đảm bảo tài liệu không bao giờ bị lệch pha so với code thực tế. Để hoàn thiện tài liệu, ta bổ sung ngữ nghĩa nghiệp vụ thông qua @Operation, @Parameter, @Schema và @ApiResponse.

1. Tại sao OpenAPI Specification 3.1 là chuẩn mực mô tả API hiện đại?

Viết tài liệu API thủ công (trên Confluence hoặc Google Docs) luôn gặp vấn đề tồn tại hai nguồn sự thật: mã nguồn và tài liệu. Khi lập trình viên sửa đổi kiểu dữ liệu mà quên cập nhật tài liệu, đội ngũ Frontend hoặc đối tác tích hợp sẽ gọi sai và mất nhiều giờ gỡ lỗi.

OpenAPI Specification 3.1 (trước đây là Swagger) giải quyết triệt để vấn đề này bằng định dạng máy đọc được:

YAML
openapi: 3.1.0
info:
  title: TaskFlow Order API
  version: 1.0.0
paths:
  /api/v1/orders/{id}:
    get:
      summary: Get order details by ID
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Order details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderDto'
        '404':
          description: Order not found

Tệp đặc tả này đóng vai trò là nguồn dữ liệu đầu vào cho toàn bộ hệ sinh thái công cụ:

  • Swagger UI: Tạo trang web tương tác cho phép chạy thử API trực tiếp trên trình duyệt.
  • OpenAPI Generator: Tự động sinh mã nguồn client SDK cho hơn 50 ngôn ngữ (TypeScript, Kotlin, Python).
  • Postman / Bruno: Tự động nhập spec thành các bộ sưu tập API kiểm thử.

2. springdoc-openapi quét annotation và dựng spec trong bộ nhớ ra sao?

Để tích hợp, bạn chỉ cần thêm một dependency duy nhất vào pom.xml:

XML
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.8.0</version>
</dependency>

Cơ chế hoạt động bên dưới:

  1. Khi ApplicationContext phát ra sự kiện ContextRefreshedEvent (toàn bộ Bean đã nạp xong), springdoc-openapi bắt đầu quét các class có gắn @RestController.
  2. Sử dụng Java Reflection để đọc metadata từ các annotation Spring Web và Bean Validation.
  3. Xây dựng một đối tượng Java OpenAPI hoàn chỉnh trong bộ nhớ RAM.

Annotation có sẵn trong controller đi xuống hộp springdoc quét bean qua reflection lúc startup, rồi tới hộp nhấn mạnh object OpenAPI 3.1 dựng trong bộ nhớ và không có file spec nào trên đĩa, từ đó rẽ thành hai đầu ra là GET v3 api-docs trả JSON và GET swagger-ui.html trả trang UI

Điểm then chốt: Không có file tĩnh nào được lưu trên đĩa. Mỗi lần ứng dụng khởi động lại, đặc tả OpenAPI được tái tạo từ chính mã nguồn đang chạy, đảm bảo độ chính xác tuyệt đối 100%.

3. Những thông tin nào springdoc tự động phát hiện và những gì cần khai báo bổ sung?

80% thông tin được tự động nhận diện:

Annotation sẵn có trong Spring MVCThành phần tương ứng trong OpenAPI
@RestController + @RequestMappingĐường dẫn paths trong tài liệu
@GetMapping, @PostMapping...HTTP method của endpoint
@PathVariable Long idParameter in: path, required: true, kiểu integer
@RequestParam(required = false)Parameter in: query, required: false
@RequestBody OrderRequest reqĐối tượng requestBody với liên kết schema
@ResponseStatus(HttpStatus.CREATED)Mã phản hồi 201 Created
@NotNull, @Size(min = 2, max = 50)Thuộc tính schema required, minLength, maxLength
Return type ResponseEntity<OrderDto>Cấu trúc dữ liệu JSON của response 200

20% thông tin cần bổ sung thủ công:

Hệ thống kiểu dữ liệu Java không thể tự suy luận được:

  • Mục đích nghiệp vụ chi tiết của endpoint.
  • Ví dụ thực tế (example) cho từng tham số và trường dữ liệu.
  • Các mã phản hồi lỗi nghiệp vụ đặc thù (409 Conflict, 422 Unprocessable Entity).

4. Làm thế nào để tùy biến tài liệu với các annotation OpenAPI?

Sử dụng các annotation trong gói io.swagger.v3.oas.annotations:

@Operation: Mô tả phương thức API

Java
@Operation(
    summary = "Lay thong tin don hang theo ID",
    description = "Tra ve chi tiet don hang kem danh sach san pham. Tra ve 404 neu khong tim thay.",
    tags = {"Orders"}
)
@GetMapping("/orders/{id}")
public OrderDto getOrderById(
    @Parameter(description = "ID dinh danh don hang", example = "1024")
    @PathVariable Long id
) {
    return orderService.findById(id);
}

@Schema: Mô tả thuộc tính DTO và dữ liệu mẫu

Java
@Schema(description = "Yeu cau tao moi don hang")
public record OrderRequest(
    @Schema(description = "Ten khach hang", example = "Nguyen Van A", minLength = 2)
    @NotBlank
    String customer,

    @Schema(description = "Tong gia tri don hang (VND)", example = "250000", minimum = "1000")
    @NotNull @Positive
    BigDecimal total,

    @Schema(description = "Ngay du kien giao hang (ISO 8601)", example = "2026-07-20")
    @NotNull @Future
    LocalDate deliveryDate
) {}
Tầm quan trọng của ví dụ dữ liệu (example)

Nếu thiếu example, Swagger UI sẽ hiển thị giá trị mặc định vô nghĩa như "customer": "string", "total": 0. Khi có ví dụ rõ ràng, tính năng "Try it out" sẽ tự động điền sẵn dữ liệu mẫu thực tế, giúp lập trình viên Frontend kiểm thử ngay lập tức mà không phải đoán định dạng.

@ApiResponse: Khai báo tường minh các kịch bản phản hồi

Java
@Operation(summary = "Tao moi don hang")
@ApiResponses({
    @ApiResponse(responseCode = "201", description = "Tao don hang thanh cong"),
    @ApiResponse(responseCode = "400", description = "Du lieu dau vao khong hop le (RFC 9457)"),
    @ApiResponse(responseCode = "409", description = "Trung ma don hang trong he thong")
})
@PostMapping("/orders")
@ResponseStatus(HttpStatus.CREATED)
public OrderDto createOrder(@Valid @RequestBody OrderRequest req) {
    return orderService.create(req);
}

5. Cấu hình thông tin toàn cục (Global Metadata) qua @Bean OpenAPI như thế nào?

Cấu hình tiêu đề, phiên bản, thông tin liên hệ và danh sách máy chủ:

Java
@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("TaskFlow Core API")
                .version("v1.0.0")
                .description("He thong quan ly du an va quy trinh nghiep vu")
                .contact(new Contact().name("OLHub Platform Team").email("[email protected]"))
                .license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0")))
            .servers(List.of(
                new Server().url("http://localhost:8080").description("Moi truong Local"),
                new Server().url("https://api-staging.olhub.org").description("Moi truong Staging"),
                new Server().url("https://api.olhub.org").description("Moi truong Production")
            ));
    }
}

Giới hạn phạm vi quét trong application.yml

Tránh để lộ các API nội bộ hoặc endpoint Actuator ra ngoài tài liệu:

YAML
springdoc:
  api-docs:
    path: /v3/api-docs
    enabled: true
  swagger-ui:
    path: /swagger-ui.html
    operationsSorter: method
    tagsSorter: alpha
    tryItOutEnabled: true
  packages-to-scan: com.olhub.taskflow.api # Chi quet cac controller public
  paths-to-match: /api/v1/**               # Chi hien thi endpoint bat dau bang /api/v1
  show-actuator: false                     # An toan: An toan bo actuator endpoints

6. Những cạm bẫy kỹ thuật thường gặp khi sử dụng springdoc-openapi là gì?

  1. Bật Swagger UI trên Production công khai: Để lộ toàn bộ tài liệu và nút bấm "Try it out" có thể tạo điều kiện cho hacker tấn công dò quét. Khuyến nghị tắt trên production thông qua springdoc.swagger-ui.enabled: false hoặc bảo vệ bằng Spring Security.
  2. Quên định dạng cho trường Date/Time: Khi không có @Schema(example = "2026-07-20"), người dùng không biết cần gửi kiểu chuỗi hay timestamp, dẫn đến lỗi 400 không đáng có.
  3. Trùng lặp Tag dẫn đến giao diện lộn xộn: Không phân nhóm tags khiến toàn bộ các endpoint dồn chung vào một danh mục theo tên controller class.

7. OpenAPI kết nối với các bài học khác trong hệ thống ra sao?

  • RestController & RequestMapping: springdoc dựa trên các annotation định tuyến này để dựng nên cây tài liệu API.
  • Jakarta Bean Validation: Tự động chuyển đổi các ràng buộc @Size, @Pattern, @NotNull thành các thuộc tính kiểm soát tương ứng trên schema JSON.
  • OpenAPI Nâng Cao: Mở rộng cấu hình xác thực JWT Bearer token và tư duy thiết kế API-first.

8. Những điểm then chốt cần ghi nhớ là gì?

  • OpenAPI 3.1 là tiêu chuẩn vàng mô tả hợp đồng REST API, thay thế hoàn toàn tài liệu viết tay.
  • springdoc-openapi tự động hóa việc sinh tài liệu từ mã nguồn bằng Reflection trong pha khởi động.
  • Mã nguồn là nguồn sự thật duy nhất (Single Source of Truth), loại bỏ nguy cơ tài liệu bị lỗi thời.
  • Tận dụng @Operation, @Parameter, @Schema và @ApiResponse để làm giàu ngữ nghĩa và cung cấp ví dụ trực quan.
  • Giới hạn phạm vi quét qua packages-to-scan và paths-to-match để bảo vệ các endpoint nội bộ.

9. Bài tập tự kiểm tra mức độ hiểu sâu

Tự kiểm tra
0/5 câu đã trả lời
  1. Q1
    Tại sao tài liệu API tự động sinh bởi springdoc-openapi lại vượt trội hơn tài liệu viết tay trên Confluence?
  2. Q2
    springdoc-openapi thực hiện quét Controller và sinh spec OpenAPI vào thời điểm nào trong vòng đời ứng dụng?
  3. Q3
    Annotation nào được sử dụng để cung cấp giá trị dữ liệu mẫu (example) cho một thuộc tính trong DTO trên giao diện Swagger UI?
  4. Q4
    Cấu hình nào trong application.yml giúp ngăn chặn các Controller nội bộ (/internal/**) bị hiển thị trên Swagger UI công khai?
  5. Q5
    Annotation nào giúp gộp các endpoint từ nhiều Controller khác nhau vào chung một nhóm hiển thị trên Swagger UI?

Bài tiếp theo: Security scheme, groups & API-first

Bài này có giúp bạn hiểu bản chất không?

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Lộ trình Junior Java Backend Developer

Chặng 15/21 · REST API với Spring MVC — bài 18/21 của chặng

Thảo luận & góp ý

Hỏi đáp về bài này

Bài tiếp theo

OpenAPI Nâng Cao: Security, Grouping và Triết Lý API-First