使用 Spring Doc 为 Spring REST API 生成 OpenAPI 3.0 文档

1、概览

文档是构建 REST API 的重要组成部分。在本教程中,我们将介绍 Spring Doc,它可简化 API 文档的生成和维护,这些文档基于 OpenAPI 3 规范,适用于 Spring Boot 3.x 应用程序。

2、设置 springdoc-openapi

Spring Boot 3.x 要求使用 springdoc-openapi version 2

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

2.1、 OpenAPI 描述的路径

正确设置依赖后,我们就可以运行应用程序,并在 /v3/api-docs 路径访问 OpenAPI 描述,这是默认路径:

http://localhost:8080/v3/api-docs

此外,我们还可以使用 springdoc.api-docs 属性在 application.properties 中自定义描述的路径。例如,我们可以将路径设置为 /api-docs

springdoc.api-docs.path=/api-docs

然后,我们就可以通过以下网址访问文档描述:

http://localhost:8080/api-docs

OpenAPI 描述定义默认为 JSON 格式。对于 yaml 格式,我们可以从以下网址获取定义:

http://localhost:8080/api-docs.yaml

3、整合 Swagger UI

除了生成 OpenAPI 3 规范之外,我们还可以将 springdoc-openapi 与 Swagger UI 集成,以与我们的 API 规范进行交互并测试端点。

Springdoc-openapi 依赖项中已经包含了 Swagger UI,因此我们可以通过如下路径访问 API 文档:

http://localhost:8080/swagger-ui/index.html

3.1、对 swagger-ui properties 的支持

springdoc-openapi 库还支持 swagger-ui properties。这些属性可作为 Spring Boot 属性使用,前缀为 springdoc.swagger-ui

例如,我们可以通过更改 application.properties 文件中的 springdoc.swagger-ui.path 属性,自定义 API 文档的路径:

springdoc.swagger-ui.path=/swagger-ui-custom.html

现在我们的 API 文档可以通过 http://localhost:8080/swagger-ui-custom.html 访问。

再比如,我们可以使用 springdoc.swagger-ui.operationsSorter 属性根据 HTTP 方法对 API 路径进行排序:

springdoc.swagger-ui.operationsSorter=method

3.2、示例 API

假设我们的应用程序有一个管理 Book 的 controller:

@RestController
@RequestMapping("/api/book")
public class BookController {

    @Autowired
    private BookRepository repository;

    @GetMapping("/{id}")
    public Book findById(@PathVariable long id) {
        return repository.findById(id)
            .orElseThrow(() -> new BookNotFoundException());
    }

    @GetMapping("/")
    public Collection<Book> findBooks() {
        return repository.getBooks();
    }

    @PutMapping("/{id}")
    @ResponseStatus(HttpStatus.OK)
    public Book updateBook(
      @PathVariable("id") final String id, @RequestBody final Book book) {
        return book;
    }
}

然后,当我们运行应用程序时,就可以在以下位置查看文档:

http://localhost:8080/swagger-ui-custom.html

Swagger UI

让我们深入到 /api/book 端点,查看其请求和响应的详细信息:

Swagger UI API 详情

4、整合 springdoc-openapi 与 Spring WebFlux

我们还可以在 Spring WebFlux 应用上轻松整合 springdoc-openapi 和 Swagger UI(springdoc.swagger-ui properties 也可用)。只需要在 pom.xml 文件中添加 springdoc-openapi-webflux-ui 依赖:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
    <version>2.1.0</version>
</dependency>

5、显示分页信息

Spring Data JPA 与 Spring MVC 无缝集成。例如,对 Pageable 的支持:

@GetMapping("/filter")
public Page<Book> filterBooks(@ParameterObject Pageable pageable) {
     return repository.getBooks(pageable);
}

自 springdoc-openapi v1.6.0 起,Pageable 支持已开箱即用。pagesizesort 查询参数会添加到生成的文档中:

分页信息

6、使用 springdoc-openapi Maven 插件

springdoc-openapi 库提供了一个 Maven 插件 springdoc-openapi-maven-plugin,可生成 JSON 和 YAML 格式的 OpenAPI 描述。

springdoc-openapi-maven-plugin 插件与 spring-boot-maven 插件配合使用。Maven 会在 integration-test 阶段运行 openapi 插件。

让我们看看如何在 pom.xml 中配置插件:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.3.3.RELEASE</version>
    <executions>
        <execution>
            <id>pre-integration-test</id>
            <goals>
                <goal>start</goal>
            </goals>
        </execution>
        <execution>
            <id>post-integration-test</id>
            <goals>
                <goal>stop</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-maven-plugin</artifactId>
    <version>1.4</version>
    <executions>
        <execution>
            <phase>integration-test</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

我们还可以对插件进行配置,以使用自定义值:

<plugin>
    <executions>
        .........
    </executions>
    <configuration> 
        <apiDocsUrl>http://localhost:8080/v3/api-docs</apiDocsUrl> 
        <outputFileName>openapi.json</outputFileName> 
        <outputDir>${project.build.directory}</outputDir> 
    </configuration>
</plugin>

插件配置的参数如下:

  • apiDocsUrl:访问 JSON 格式文档的 URL,默认为 http://localhost:8080/v3/api-docs
  • outputFileName:存储定义的文件名;默认为 openapi.json
  • outputDir:文档存储目录的绝对路径;默认为 ${project.build.directory}

7、使用 JSR-303 Bean Validation 自动生成文档

当我们的 model 包含 JSR-303 Bean 验证注解(如 @NotNull@NotBlank@Size@Min@Max)时,springdoc-openapi 库会使用它们为相应的约束生成额外的 schema 文档。

让我们用图书 Book bean 来举个例子:

public class Book {

    private long id;

    @NotBlank
    @Size(min = 0, max = 20)
    private String title;

    @NotBlank
    @Size(min = 0, max = 30)
    private String author;

}

现在,为 Book Bean 生成的文档信息量更大了:

添加了 Bean Validation 后的 Book Schema

8、使用 @ControllerAdvice@ResponseStatus 生成文档

@RestControllerAdvice 类中的方法上使用 @ResponseStatus 会自动为 response code 生成文档。在这个 @RestControllerAdvice 类中,两个方法都注解了 @ResponseStatus

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(ConversionFailedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<String> handleConversion(RuntimeException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<String> handleBookNotFound(RuntimeException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

因此,我们现在可以看到 response code 400404 的文档:

使用 @ControllerAdvice 和 @ResponseStatus 生成的文档

9、使用 @Operation@ApiResponses 生成文档

接下来,让我们看看如何使用几个 OpenAPI 专用注解 为我们的 API 添加一些说明。

我们使用 @Operation@ApiResponses 对 controller 的 /api/book/{id} 端点进行注解:

@Operation(summary = "Get a book by its id")
@ApiResponses(value = { 
  @ApiResponse(responseCode = "200", description = "Found the book", 
    content = { @Content(mediaType = "application/json", 
      schema = @Schema(implementation = Book.class)) }),
  @ApiResponse(responseCode = "400", description = "Invalid id supplied", 
    content = @Content), 
  @ApiResponse(responseCode = "404", description = "Book not found", 
    content = @Content) })
@GetMapping("/{id}")
public Book findById(@Parameter(description = "id of book to be searched") 
  @PathVariable long id) {
    return repository.findById(id).orElseThrow(() -> new BookNotFoundException());
}

效果如下:

使用 @Operation 和 @ApiResponses 生成的文档

我们可以看到,我们添加到 @Operation 中的文本被置于 API 操作层。同样,在 @ApiResponses 容器注解中添加到各种 @ApiResponse 元素中的说明也在这里可见,从而为我们的 API 响应增添了意义。

我们没有为上述 400404 响应定义任何 schema。由于我们为它们定义了一个空 @Content,因此只显示了它们的描述。

11、对 Kotlin 的支持

Spring Boot 2.x 起,就为 Kotlin 提供了一流的支持。由于我们使用的是 Spring Boot 3.x 版本,因此 SpringDoc 支持用 Kotlin 编写的应用程序。

我们将在 Kotlin 中创建一个简单的 Foo API,以了解其实际效果。

完成初始设置后,我们将添加一个 data class 和一个 controller。我们将把它们添加到 Boot 应用程序的一个子包中,这样当程序运行时,它就会同时加载我们的 FooController 和之前的 BookController

@Entity
data class Foo(
    @Id
    val id: Long = 0,
    
    @NotBlank
    @Size(min = 0, max = 50)
    val name: String = ""
)

@RestController
@RequestMapping("/")
class FooController() {
    val fooList: List = listOf(Foo(1, "one"), Foo(2, "two"))

    @Operation(summary = "Get all foos")
    @ApiResponses(value = [
    ApiResponse(responseCode = "200", description = "Found Foos", content = [
            (Content(mediaType = "application/json", array = (
                ArraySchema(schema = Schema(implementation = Foo::class)))))]),
    ApiResponse(responseCode = "400", description = "Bad request", content = [Content()]),
    ApiResponse(responseCode = "404", description = "Did not find any Foos", content = [Content()])]
    )
    @GetMapping("/foo")
    fun getAllFoos(): List = fooList
}

现在,当我们点击 API 文档 URL 时,也会看到 Foo API:

kotlin FooAPI  的文档

为了增强对 Kotlin 类型的支持,我们可以添加此依赖项:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-common</artifactId
    <version>2.1.0</version>
</dependency>

之后,我们的 Foo schema 显示的信息更加详细,就像我们添加 JSR-303 Bean Validation 时一样:

信息更加详细的 Foo Schema

11、总结

在本文中,我们学习了在项目中设置 springdoc-openapi。然后,我们了解了如何将 springdoc-openapi 与 Swagger UI 集成。最后,我们还了解了如何在 Spring Webflux 项目中实现这一功能。

接下来,我们使用 springdoc-openapi Maven 插件为我们的 API 生成 OpenAPI 定义,并了解了如何从 Spring Data 公开分页和排序信息。之后,我们了解了 springdoc-openapi 如何使用 JSR 303 Bean validation 注解和 @ControllerAdvice 类中的 @ResponseStatus 注解自动生成文档。

我们还学习了如何使用一些 OpenAPI 特有的注解为我们的 API 添加说明。最后,我们还了解了 OpenAPI 对 Kotlin 的支持。

Springdoc-openapi 可根据 OpenAPI 3 规范生成 API 文档。此外,它还为我们处理了 Swagger UI 配置,使 API 文档的生成变得相当简单。


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