Tạo API document với Swagger 2 trong Spring Boot REST API

Các ứng dụng web ngày nay được chia thành 2 phần front-end và back-end được phát riêng biệt. Thông thường, hệ thống back-end sẽ được cung cấp các API cho front-end sử dụng hoặc các bên thứ 3 muốn liên kết với hệ thống. 

Vì 2 hệ thống được phát triển độc lập, nên việc sử dụng API của back-end phải thông qua các tài liệu mô tả về các API URL, tham số đầu vào, kết quả và status trả về etc. Do vậy những tài liệu này phải được update khi backend sửa đổi hay thêm các tính năng mới, về lâu dài công việc này hết sức tẻ nhạt và tốn nhiều công sức.

Chúng ta cần một cơ chế tự động sẽ sinh ra các document mô tả API của backend mà không cần phải viết và chỉnh sửa một cách thủ công. Đó là lý do tại sao chúng ta cần đến Swagger 2 để triển khai công việc này.

Maven dependency

Để sử dụng Swagger trong Spring Boot, chúng ta cần thêm springfox-swagger2 vào POM.XML.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
@Configuration
public class SpringFoxConfig {                                    
    @Bean
    public Docket api() { 
        return new Docket(DocumentationType.SWAGGER_2)  
          .select()                                  
          .apis(RequestHandlerSelectors.any())              
          .paths(PathSelectors.any())                          
          .build();                                           
    }
}

 Java Configuration

Cấu hình của Swagger chủ yếu xoay quanh Docket bean

@Configuration
@EnableSwagger2
public class SpringFoxConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
    }
}

Gỉa sử mình có BookController chứa các API dùng để thao tác với Book entity

public class Book {

    private String id;
    private String name;
    private String author;
    private Integer price;

    contructor, setter, getter 
}
@RestController
@RequestMapping("/book")
public class BookController {

    @GetMapping("/")
    public List<Book> getAllBook() {
        return Arrays.asList(new Book("1", "Book A", "Deft", 10000));
    }


    @GetMapping("/{id}")
    public Book getBookById(@PathVariable String id) {
        return new Book(id, "Book A", "Deft", 10000);
    }

    @PostMapping("/")
    public void create(@RequestBody Book book) {
        System.out.println(book.getId());
    }

    @PutMapping("/")
    public void update(@RequestBody Book book) {
        System.out.println(book.getId());
    }
}

Sau khi Docket bean được khởi tạo, phương thức select () của nó trả về một ApiSelectorBuilder instance, cung cấp cơ chế để kiểm soát các API bởi Swagger.

Chúng ta có thể cấu hình các vị từ để chọn RequestHandlers với sự trợ giúp của RequestHandlerSelectorsPathSelectors. Sử dụng any() cho cả hai sẽ giúp chúng ta tạo document cho toàn bộ các API thông qua Swagger.

Sau khi hoàn thành các bước trên, các bạn có thể truy cập vào đường link: http://localhost:8080/v2/api-docstrình duyệt sẽ hiển thị JSON chứa thông tin về các API trong dự án.

Chúng ta có thể thấy rằng với kết quả trả về JSON như vậy sẽ rất khó để đọc hiểu, các dev thì có khi đọc còn hiểu nhưng vẫn rất rối mắt. Còn nếu đưa cho tester cái đống JSON bảo họ test API đi thì có mà ăn vả.

À, chúng ta có một giải pháp để chuyển hoá đống JSON thành giao diện web dễ nhìn, dễ hiểu và có thể kiểm thử trực tiếp với Swagger UI mà chúng ta sẽ tìm hiểu ở phần sau.

Swagger UI

Trước tiên, để sử dụng Swagger UI thì chúng ta cần thêm dependency của nó vào

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Sau khi biên Install và chạy lại dự án, giờ đây các bạn có thể vào đường dẫn http://localhost:8080/swagger-ui.html để xem API document thông qua giao diện web.

Một tính năng nữa khá tuyệt với với Swagger UI là chúng ta có thể kiểm thử API trực tiếp trên web này mà không cần đến các tool khác như Postman, curl, etc.

Để kiểm thử trực tiếp trên đây, các bạn chọn đến API mong muốn và chọn vào Try it out và sau đó tiến hành nhập các thông tin cần thiết rồi submit.

Data validation

Qua các bước ở trên, thì có lẽ document ui đã hoạt động, tuy nhiên chúng ta cần tiến hành thêm một số bước nữa để hoàn thiện phần API docs này hơn với data validation. 

Nếu như ở phần trên chúng ta chỉ biết là những thông tin nào cần để gửi lên API, nhưng các yêu cầu dữ liệu của chúng thì chưa được thể hiển, ví dụ như ID thì không được NULL, Price thì không được bé hơn 0, email thì phải đúng định dạng format. 

Trước khi làm data validation, các bạn cần đảm bảo mình đã có validation-api dependency trong POM.XML

<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>2.0.1.Final</version>
</dependency>

Tiếp theo chúng ta cần thêm springfox-bean-validators dependency chứa các cấu hình cần thiết để triển khai validate dữ liệu.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-bean-validators</artifactId>
    <version>2.9.2</version>
</dependency>

Bây giờ, chúng ta đã sẵn sàng để thêm các data validation vào các Model sử dụng trong REST API. 

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class Book {

    @NotNull(message = "ID can't be null")
    private String id;

    @NotNull(message = "Name can't be null")
    private String name;

    private String author;

    @Min(value = 0, message = "Min Price can't be positive")
    private Integer price;
    // constructor, setter, getter
}

Cuối cùng, chúng ta cần import BeanValidatorPluginsConfiguration class vào SpringFoxConfig

@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
    }
}

Khởi động lại ứng dụng các bạn có thể thấy trên giao diện đã hiển thị các validate dữ liệu tương ứng định nghĩa trong model.

Custom Swagger

Không phải lúc nào chúng ta cũng mong muốn công khai tất cả các API trong backend, ví dụ như các API cho admin user, thông thường những người làm backend sẽ làm luôn frontend cho admin vì thế không có lý do gì mà họ muốn công khai cho ra bên ngoài vì vấn đề bảo mật.

Filtering API

Ở trên, chúng ta đã sử dụng RequestHandlerSelectors.any() cho phép công khai tất cả các API. Nhưng nó còn có thể cho phép lọc các API theo package, class annotation và method annotation.

PathSelectors cung cấp thêm cơ chế bổ sung với các prefix API URL thông qua các hàm any(), none(), regex() ant().

Trong ví dụ dưới đây, mình sẽ hướng dẫn Swagger chỉ quét các API từ một package cụ thể, với các đường dẫn cụ thể, sử dụng ant().

@Bean
public Docket filterAPI() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.deft.swagger"))
            .paths(PathSelectors.ant("/book/*"))
            .build();
}

Ở đoạn ví dụ trên, thì Swagger chỉ quét các API tạo package com.deft.swagger và chỉ quét các API có đường dẫn bắt đầu bằng /book.

API Information

Swagger cung cấp một số thông tin mặc định khi chúng ta không cung cấp các thông tin về API Information như địa chỉ email liên hệ, License, website etc.

Chúng ta có thể custom bằng cách sử dụng apiInfo(ApiInfo apiInfo) trong Docket bean config.

@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig 

    @Bean
    public Docket filterAPI() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.deft.swagger"))
                .paths(PathSelectors.ant("/book/*"))
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfo(
                "Swagger API DEMO",
                "Some custom description of API.",
                "API TOS",
                "Terms of service",
                new Contact("Deft", "https://shareprogramming.net//", "[email protected]"),
                "License of API", "API license URL", Collections.emptyList());
    }
}

Giờ đây thông tin về API đã được thay đổi tương ứng với những gì chúng ta đã cấu hình trong mã nguồn.

Custom Response Message

Mặc định, Swagger cung cấp các message mặc định cho từng HTTP_STATUS_CODE. Tuy nhiên chúng ta có thể custom lại thông qua globalResponseMessage() thuộc Docket bean.

Giả sử chúng ta muốn ghi đè các thông báo phản hồi 500 và 403 cho tất cả các phương thức GET.

@Bean
public Docket filterAPI() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.deft.swagger"))
            .paths(PathSelectors.ant("/book/*"))
            .build()
            .apiInfo(apiInfo())
            .useDefaultResponseMessages(false)
            .globalResponseMessage(RequestMethod.GET,
                    Arrays.asList(new ResponseMessageBuilder()
                                    .code(500)
                                    .message("500 message custom")
                                    .responseModel(new ModelRef("Error"))
                                    .build(),
                            new ResponseMessageBuilder()
                                    .code(403)
                                    .message("403 message custom!")
                                    .build()));
}

Kết quả 

Kết bài

Như vậy là chúng ta đã thấy rằng sự quan động của API Document rồi phải không nào. Chứ không thể nào mà ông frontend cứ xách mông đi hỏi ông backend các API cần gì, trả về gì như vậy sẽ rất tốn thời gian của nhau. 

Một lưu ý rằng khi lên production thì nên tất swagger api đi nhé, không để người khác vào xem được thì có khả năng bị hack hệ thống đấy.

Sau cùng, mã nguồn được mình công khai trên gitlab, các bạn có thể tham khảo nếu cần: springfox

Nguồn tham khảo

https://www.baeldung.com/spring-rest-openapi-documentation

https://www.vojtechruzicka.com/documenting-spring-boot-rest-api-swagger-springfox/

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x