知識庫 / Spring RSS 訂閱

使用 OpenAPI 3.0 文檔 Spring REST API

REST,Spring
HongKong
6
03:47 AM · Dec 06 ,2025

1. 概述

文檔是構建 REST API 的關鍵組成部分。在本教程中,我們將探討 SpringDoc,它簡化了基於 OpenAPI 3 規範為 Spring Boot 3.x 應用生成和維護 API 文檔的過程。

2. 設置 springdoc-openapi

Spring Boot 3.x 需要使用 版本 2springdoc-openapi:

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

進一步,Springdoc OpenAPI 版本必須與 Spring Boot 版本兼容,如Springdoc 官方兼容矩陣所示兼容矩陣

2.1. OpenAPI 描述路徑

在正確設置依賴後,我們可以運行我們的應用程序,並在 /v3/api-docs</em/> 中找到 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 屬性

springdoc-openapi 庫也支持 swagger-ui 屬性。 這些屬性可以作為 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 對象的控制器:

@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

讓我們深入到 /api/book 端點,查看其請求和響應的詳細信息:


4. 與 springdoc-openapi 集成 Spring WebFlux

我們還可以為 Spring WebFlux 應用程序啓用 springdoc.swagger-ui 屬性。 這允許我們輕鬆地在 Spring WebFlux 應用程序中集成 springdoc-openapi 和 Swagger UI。 要啓用此功能,我們將在 pom.xml 文件中添加 springdoc-openapi-webflux-ui 依賴項。

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

5. 使用標籤進行組織

OpenAPI 規範使用標籤將 API 分組到不同的類別和組中。每個 API 操作可以有多個標籤,Swagger UI 將根據這些標籤將操作分組顯示。

我們可以定義 Swagger 的 openapi.yaml 定義文件中標籤。為了演示如何排序標籤,讓我們使用在方法和類級別上的 @Tag 註解來指定它們。

讓我們以書籍 API 為例,並在控制器類和其方法上指定標籤:

@Tag(name = "tag_at_class_level", description = "Books related class level tag")
public class BooksController {

    @Tag(name = "create")
    @Tag(name = "common_tag_at_method_level")
    @Tag(name = "createBook")
    @PostMapping(path = "/book")
    @io.swagger.v3.oas.annotations.parameters.RequestBody(required = true)
    public Book book(@Valid @RequestBody Book book) {

        return book;
    }

    @Tag(name = "find")
    @Tag(name = "common_tag_at_method_level")
    @Tag(name = "findBook", description = "Find Books related tag")
    @GetMapping(path = "/findBookById")
    public List findById(@RequestParam(name = "id", required = true) 
      @NotNull @NotBlank @Size(max = 10) long id) {
        List bookList = new ArrayList<>();
	Book book = new Book();

	book.setId(1);
	bookList.add(book);
	return bookList;
    }

    @Tag(name = "delete") 
    @Tag(name = "common_tag_at_method_level")
    @Tag(name = "deleteBook")
    @DeleteMapping(path = "/deleteBookById")
    public long deleteById(@RequestParam(name = "id", required = true) 
      @NotNull @NotBlank @Size(max = 10) long id) {

        return id;
    }

    @Tag(name = "update")
    @Tag(name = "common_tag_at_method_level")
    @Tag(name = "updateBook")
    @PutMapping(path = "/updateBookById")
    public long updateById(@RequestParam(name = "id", required = true) 
      @NotNull @NotBlank @Size(max = 10) long id) {
        return id;
    }
}

5.1. 默認標籤順序

當運行應用程序時,Swagger UI (http://localhost:8080/swagger-ui.html) 會按照其默認顯示順序顯示標籤,並且某些附加條件會優先生效:

默認顯示順序基於操作名稱: PUT, POST, GET, DELETE,按該順序排列。換句話説,PUT 操作相關的標籤會在 POST 操作相關的標籤之前顯示,以此類推。

原因是 tag_at_class_level 位於第一個標籤是因為 @Tag 註解會將標籤添加到 openAPI.tags 字段,從而在應用了 @Tag 註解(例如,帶有 descriptionexternalDocs 等附加字段)時,確定全局標籤排序。同樣,findBook 標籤列在第二個位置。但是,如果僅提供 name,則標籤僅應用於操作,不會影響全局標籤排序。

此外,每個操作都會添加一個類級別標籤,這也是為什麼 tag_at_class_level 標籤應用於每個操作的原因。當標籤應用於多個操作時,這些操作按 PUT, POST, GET, 和 DELETE 順序排列。

正如排序所示,PUT 相關的標籤會在全局優先的標籤之後排列。讓我們顯示其他標籤,以發現 common_tag_at_method_level 位於下一個位置,因為它是一個 PUT 相關的標籤:

PUT 相關的標籤之後,POST 相關的標籤按順序排列。讓我們顯示剩餘的標籤,以發現剩餘的 GETDELETE 相關的標籤按該順序排列:

如上所示,Swagger UI 的默認顯示順序可能不符合我們的需求。 鑑於此,讓我們看看如何控制此顯示順序的一些方法。

5.2. 使用 tagsSorter 屬性

首先,我們可以通過在 Spring 屬性文件中聲明一個屬性來配置排序,例如在 application.properties 中:

springdoc.swagger-ui.tagsSorter=alpha

該屬性指示 Swagger UI 在其顯示中按標籤組的字母順序進行排序。

讓我們再次運行相同的應用程序並啓動 Swagger UI。 這一次,標籤的排序是基於標籤名稱的字母順序:

再次,當一個標籤應用於多個操作時,該標籤下的操作按 PUT, POST, GET, 和 DELETE 順序排列。

為了驗證基於標籤名稱的字母順序排序,讓我們同時顯示剩餘的標籤:

或者,我們可以將該屬性的值設置為一個排序函數。

5.3. 使用 writer-with-order-by-keys 屬性

或者,我們也可以使用 springdoc.writer-with-order-by-keys 屬性來指定標籤的排序,該屬性也位於 application.properties 中:

springdoc.writer-with-order-by-keys=true

此屬性具有獨特性,因為它不特定於 Swagger UI。它基於路徑按字母順序排序來確定標籤順序。 但是,它仍然尊重openAPI.tags 字段中定義的全局標籤,並在所有標籤之前列出它們。 讓我們通過運行應用程序並啓動 Swagger UI 來驗證排序:

在優先顯示的全局標籤之後,它按路徑字母順序對標籤進行排序。 因此,它首先列出與 /book 路徑關聯的操作的標籤,然後是與 /deleteBookById 路徑關聯的操作的標籤:

讓我們列出剩餘的標籤以驗證標籤按路徑名稱排序,並按字母順序排列:

5.4. 使用全局 標籤 部分

此外,我們還可以使用全局 標籤 部分在我們的 openapi.yaml 定義文件的根級別中定義標籤的排序順序:

tags:
  - name: books
    description: Everything about your books
    externalDocs:
      url: http://docs.my-api.com/books.htm
  - name: create
    description: Add a book to the inventory 
    externalDocs:
      url: http://docs.my-api.com/add-book.htm

這告訴 Swagger UI 首先顯示 books 標籤,然後顯示 create 標籤。當我們的訂單不是按字母順序排列,或者無法通過排序函數輕鬆描述時,這非常實用。

或者,我們可以使用 @OpenAPIDefinition 註解,在規範級別上程序化地設置這些標籤。讓我們使用相同的控制器類並使用 @OpenAPIDefinition 註解:

@OpenAPIDefinition(tags = {
     @Tag(name = "create", description = "Add book to inventory"),
     @Tag(name = "delete", description = "Delete book from inventory"),
     @Tag(name = "find", description = "Find book from inventory"),
     @Tag(name = "update", description = "Update book in inventory"),
     @Tag(name = "createBook", description = "Add book to inventory"),
     @Tag(name = "deleteBook", description = "Delete book from inventory"),
     @Tag(name = "findBook", description = "Find book from inventory"),
     @Tag(name = "updateBook", description = "Update book in inventory")
})
public class BooksController_2 { ... }

讓我們再次運行該應用程序並啓動 Swagger UI。 Swagger UI 會按照聲明的順序顯示這些標籤

讓我們列出剩餘的標籤以驗證它們是否按我們指定的順序排列:

值得注意的是,當我們使用 @OpenAPIDefinition 註解時,我們不使用任何屬性來排序標籤。

6. 暴露分頁信息

Spring Data JPA 與 Spring MVC 集成非常順暢。其中一個示例就是 Pageable 支持:

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

Pageable 的支持自 springdoc-openapi v1.6.0 版本起就已內置提供。生成的文檔中會添加 pagesizesort 查詢參數。

7. 使用 springdoc-openapi Maven 插件

springdoc-openapi 庫提供 Maven 插件 springdoc-openapi-maven-plugin,它可以生成 OpenAPI 描述文件,格式為 JSON 和 YAML。

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>3.4.3</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}

8. 使用 JSR-303 Bean 驗證自動生成文檔

當我們的模型包含 JSR-303 Bean 驗證註解,例如 @NotNull@NotBlank@Size@Min@Max 時,springdoc-openapi 庫會利用這些註解來為相應的約束生成額外的模式文檔。

讓我們通過使用我們的 Book 實體來舉例説明:

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 豆的生成文檔更加具有信息量:

9. 使用 @ControllerAdvice@ResponseStatus 生成文檔

使用 @ResponseStatus@RestControllerAdvice 類的方法中,將自動生成響應代碼的文檔。 在此 @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);
    }
}

因此,我們現在可以看到對響應碼 400 和 404 的文檔。

10. 使用 @Operation@ApiResponses 生成文檔

接下來,讓我們看看如何使用 OpenAPI 特定的註解為我們的 API 添加描述。

要做到這一點,我們將使用 @Operation@ApiResponses 註解來標註控制器的 /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 的文本位於 API 操作級別。 類似地,添加到各種 @ApiResponse 元素中的 @ApiResponses 容器註解中的描述也在此處可見,從而賦予我們的 API 響應意義。

我們沒有獲取響應 400 和 404 的 Schema。 因為我們定義了空的 @Content 用於它們,所以只顯示它們的描述。

11. 描述請求體

當定義接受請求體(request body)的端點時,我們可以使用 OpenAPI 註解中的 <em/>@RequestBody</em/> 來在 API 文檔中描述預期的請求體內容。

例如,如果我們將一個 `Book</em/> 對象傳遞給我們的 API 以創建或更新書籍,我們可以對請求體進行標註以進行文檔記錄:

@Operation(summary = "Create a new book")
@ApiResponses(value = { 
   @ApiResponse(responseCode = "201", description = "Book created successfully",
    content = { @Content(mediaType = "application/json",
      schema = @Schema(implementation = Book.class)) }),
  @ApiResponse(responseCode = "400", description = "Invalid input provided") })
@PostMapping("/")
public Book createBook(@io.swagger.v3.oas.annotations.parameters.RequestBody(
    description = "Book to create", required = true,
    content = @Content(mediaType = "application/json",
      schema = @Schema(implementation = Book.class),
      examples = @ExampleObject(value = "{ \"title\": \"New Book\", \"author\": \"Author Name\" }")))
    @RequestBody Book book) {
    return repository.save(book);
}

此配置提供了請求體詳細文檔,包括示例和模式驗證,並允許API消費者更好地理解需要發送的數據。此外,我們注意到代碼中使用了兩個@RequestBody註解:

  1. Spring 的@RequestBody:這是在Spring Boot應用程序中使端點功能正常工作的必需條件,因為它將HTTP請求體綁定到方法參數上
  2. OpenAPI 的@RequestBody:這在OpenAPI定義中記錄了請求體,併為API消費者提供了詳細描述、模式驗證和示例

最後,讓我們查看此請求的生成文檔:

我們可以看到,註解中的具體信息會包含在文檔中,例如描述、示例對象和其他元數據,這些元數據位於OpenAPI@RequestBody註解中。

12. Kotlin 支持

Spring Boot 2.x 對 Kotlin 提供了原生支持。由於我們使用的是 Spring Boot 3.x 版本,SpringDoc 支持以 Kotlin 編寫的應用程序

我們將創建一個簡單的 Foo API,以展示其功能。

在初始設置之後,我們將添加一個數據類和一個控制器。我們將它們放在 Boot App 的子包中,以便在運行時,它能夠拾取我們的 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 類型支持,我們可以添加以下依賴項:

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

在那之後,我們的 Foo 模式將更加具有信息量,正如我們在添加 JSR-303 Bean 驗證時所見:

13. 結論

在本文中,我們學習瞭如何在我們的項目中設置 springdoc-openapi。然後,我們看到了如何將 springdoc-openapi 與 Swagger UI 集成。最後,我們還看到了如何在 Spring Webflux 項目中使用它。

接下來,我們使用 springdoc-openapi Maven 插件為我們的 API 生成 OpenAPI 定義,並看到了如何從 Spring Data 中暴露分頁和排序信息。之後,我們研究了 springdoc-openapi 如何使用 JSR 303 bean 驗證註解和 註解在 類中自動生成文檔。

我們還學習瞭如何使用一些 OpenAPI 專用的註解為我們的 API 添加描述。最後,我們對 OpenAPI 的 Kotlin 支持進行了瞭解。

springdoc-openapi 按照 OpenAPI 3 規範生成 API 文檔。 此外,它還為我們處理了 Swagger UI 的配置,使 API 文檔生成成為一項相對簡單的任務。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.