0

[Java Backend Zero to Hello] BÀI 6.2: API DOCUMENTATION (OPENAPI/SWAGGER)

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 6: REST API & Best Practices | Bài 61/86


BÀI 6.2: API DOCUMENTATION (OPENAPI/SWAGGER)

Mục tiêu

  • Sử dụng SpringDoc OpenAPI
  • Tạo API documentation tự động
  • Tùy chỉnh Swagger UI
  • Mô tả API chi tiết

1. OPENAPI LÀ GÌ?

OpenAPI (trước đây là Swagger) là chuẩn mô tả API, cho phép:

  • Tự động generate documentation
  • Test API trực tiếp trên browser
  • Generate client code
  • Validate request/response

2. DEPENDENCY

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

Truy cập:

  • Swagger UI: http://localhost:8080/swagger-ui.html
  • OpenAPI JSON: http://localhost:8080/v3/api-docs

3. CẤU HÌNH CƠ BẢN

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("My API")
                .version("1.0.0")
                .description("API documentation for MyApp")
                .contact(new Contact()
                    .name("Support")
                    .email("support@example.com"))
                .license(new License()
                    .name("MIT")
                    .url("https://opensource.org/licenses/MIT")))
            .servers(List.of(
                new Server().url("http://localhost:8080").description("Local"),
                new Server().url("https://api.example.com").description("Production")
            ));
    }
}

4. ANNOTATION TRÊN CONTROLLER

@RestController
@RequestMapping("/api/users")
@Tag(name = "User", description = "User management APIs")
public class UserController {

    @Operation(
        summary = "Get all users",
        description = "Returns a list of all users with pagination"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "Success"),
        @ApiResponse(responseCode = "401", description = "Unauthorized")
    })
    @GetMapping
    public Page<UserResponse> getAll(
        @Parameter(description = "Page number", example = "0")
        @RequestParam(defaultValue = "0") int page,

        @Parameter(description = "Page size", example = "10")
        @RequestParam(defaultValue = "10") int size
    ) {
        return userService.findAll(page, size);
    }

    @Operation(summary = "Get user by ID")
    @GetMapping("/{id}")
    public UserResponse getById(
        @Parameter(description = "User ID", required = true)
        @PathVariable Long id
    ) {
        return userService.findById(id);
    }

    @Operation(summary = "Create new user")
    @PostMapping
    public ResponseEntity<UserResponse> create(
        @io.swagger.v3.oas.annotations.parameters.RequestBody(
            description = "User to create",
            required = true,
            content = @Content(schema = @Schema(implementation = CreateUserRequest.class))
        )
        @RequestBody @Valid CreateUserRequest request
    ) {
        UserResponse created = userService.create(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(created);
    }
}

5. ANNOTATION TRÊN DTO

@Schema(description = "User creation request")
public record CreateUserRequest(

    @Schema(description = "User name", example = "An", requiredMode = RequiredMode.REQUIRED)
    @NotBlank String name,

    @Schema(description = "Email address", example = "an@example.com")
    @Email String email,

    @Schema(description = "Age", example = "25", minimum = "0", maximum = "150")
    @Min(0) @Max(150) Integer age
) {}

6. JWT AUTHENTICATION TRONG SWAGGER

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        final String securitySchemeName = "bearerAuth";

        return new OpenAPI()
            .info(new Info().title("My API").version("1.0"))
            .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
            .components(new Components()
                .addSecuritySchemes(securitySchemeName,
                    new SecurityScheme()
                        .name(securitySchemeName)
                        .type(SecurityScheme.Type.HTTP)
                        .scheme("bearer")
                        .bearerFormat("JWT")));
    }
}

7. NHÓM API VỚI @TAG

@Tag(name = "Authentication", description = "Login, register APIs")
@RestController
@RequestMapping("/api/auth")
public class AuthController { ... }

@Tag(name = "User", description = "User management APIs")
@RestController
@RequestMapping("/api/users")
public class UserController { ... }

8. CẤU HÌNH SWAGGER UI

springdoc:
  swagger-ui:
    path: /swagger-ui.html
    operations-sorter: alpha
    tags-sorter: alpha
    display-request-duration: true
    filter: true
  api-docs:
    path: /v3/api-docs
  show-actuator: false
  packages-to-scan: com.example.controller

9. BẢO MẬT SWAGGER UI

@Configuration
public class SwaggerSecurityConfig {

    @Bean
    public SecurityFilterChain swaggerSecurity(HttpSecurity http) throws Exception {
        http.securityMatcher("/swagger-ui/**", "/v3/api-docs/**")
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/v3/api-docs/**").permitAll()
                .requestMatchers("/swagger-ui/**").hasRole("ADMIN")
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

10. CUSTOM ENDPOINT

@Hidden  // Ẩn khỏi Swagger
@GetMapping("/internal")
public String internal() { ... }

11. VÍ DỤ HOÀN CHỈNH

@RestController
@RequestMapping("/api/v1/products")
@Tag(name = "Product", description = "Product management APIs")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService productService;

    @GetMapping
    @Operation(summary = "List all products")
    public Page<ProductResponse> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(required = false) String category) {
        return productService.findAll(page, size, category);
    }

    @GetMapping("/{id}")
    @Operation(summary = "Get product by ID")
    public ProductResponse getById(@PathVariable Long id) {
        return productService.findById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    @Operation(summary = "Create new product")
    public ProductResponse create(@RequestBody @Valid CreateProductRequest request) {
        return productService.create(request);
    }

    @PutMapping("/{id}")
    @Operation(summary = "Update product")
    public ProductResponse update(@PathVariable Long id,
                                   @RequestBody @Valid UpdateProductRequest request) {
        return productService.update(id, request);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @Operation(summary = "Delete product")
    public void delete(@PathVariable Long id) {
        productService.delete(id);
    }
}

12. BÀI TẬP THỰC HÀNH

Bài 1: Document User API

Thêm Swagger annotations cho UserController.

Bài 2: JWT Security

Cấu hình JWT authentication trong Swagger UI.

Bài 3: Custom Info

Tùy chỉnh thông tin API (title, version, contact).


13. TÓM TẮT

Annotation Mô tả
@Tag Nhóm API
@Operation Mô tả endpoint
@ApiResponse Mô tả response
@Parameter Mô tả parameter
@Schema Mô tả schema
@Hidden Ẩn endpoint
springdoc.swagger-ui.path Đường dẫn UI

Bài tiếp theo: 6.3 DTO & Mapper


🧭 Điều Hướng Series

⬅️ Bài trước: BÀI 6.1: RESTFUL API DESIGN

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: BÀI 6.3: DTO & MAPPER


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí