使用 Spring REST 文件記錄查詢參數
1. 概述
文件對於我們打算與世界共享的任何程式碼都至關重要,尤其是當程式碼相對複雜時。好的API文件不僅能吸引開發者使用,還能展現產品的品質。一家文檔編寫草率的公司也可能有一個 API 編寫草率。
然而,開發人員喜歡為機器編寫程式碼,而不是為人編寫文字。
在本教程中,我們將探索如何將編寫文件和編寫 API 與 Spring REST 文件結合。我們將以查詢參數文件為例。
2. 介面
讓我們考慮一個具有單一端點的簡單 API:
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService service;
public BookController(BookService service) {
this.service = service;
}
@GetMapping
public List<Book> getBooks(@RequestParam(name = "page") Integer page) {
return service.getBooks(page);
}
}
此端點傳回我們網站上可用的書籍集合。但由於圖書數量較多,我們無法全部歸還。客戶提供我們目錄的頁碼,我們僅向他們發送該頁的資訊。
我們決定將此參數設為必要。在本例中,這是預設值。透過這種方式,我們提高了服務的效能,並且不允許客戶一次要求太多資料。
但是,我們必須提供有關我們決定的信息,並解釋客戶應遵循的規則。在這種情況下,如果參數不存在,客戶端會收到錯誤訊息。
3. 文件
編寫文檔的通常方法是編寫文檔,這意味著開發人員必須將同一內容編寫兩次。首先在程式碼中,然後在文字中,解釋如何與系統互動。然而,這是一種浪費,我們不能假設所有開發人員都遵循這一點。
文件是相當正式的文檔,其目的是為了清晰,而不是鼓舞人心的見解、巧妙的措辭或創新的情節結構。那麼,我們為什麼不從程式碼產生文件呢?這樣,我們就不會重複編寫相同的內容,並且所有變更都會反映在文件中。
Spring REST Docs 正是這樣做的。但是,它不是從程式碼產生文檔,因為它沒有提供太多上下文,而是從測試生成文檔。這樣,我們就可以表達相當複雜的案例和例子。另一個好處是,如果我們的測試失敗,則不會產生文件。
4. 使用文件進行測試
Spring REST 文件支援 REST 測試的主要測試框架。我們將考慮MockMvc
、 WebTestClient
和 REST-assured 的範例。然而,它們的主要思想和結構都是相似的。
此外,我們將使用 JUnit 5 作為測試案例的基礎,但也可以使用JUnit 4設定 Spring REST 文件。
以下所有測試方法都需要額外的擴充:
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
這些是用於文件產生的特殊類別。
4.1. WebTestClient
讓我們從WebTestClient
開始,這是一種更現代的 REST 測試方法。正如前面提到的,我們需要使用額外的擴充來擴展測試類別。另外,我們還需要對其進行配置:
@BeforeEach
public void setUp(ApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
this.webTestClient = WebTestClient.bindToApplicationContext(webApplicationContext)
.configureClient()
.filter(documentationConfiguration(restDocumentation))
.build();
}
之後,我們可以編寫一個測試,不僅檢查我們的 API,還提供有關請求的資訊:
@Test
@WithMockUser
void givenEndpoint_whenSendGetRequest_thenSuccessfulResponse() {
webTestClient.get().uri("/books?page=2")
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("books",
requestParameters(parameterWithName("page").description("The page to retrieve"))));
}
4.2. WebMvcTest
和MockMvc
總的來說,這種方法與前一種方法非常相似。它還需要正確的設定:
@BeforeEach
public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
this.mockMvc = webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation))
.alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
.build();
}
測試方法看起來是一樣的,除了我們使用MockMvc
及其 API:
@Test
void givenEndpoint_whenSendGetRequest_thenSuccessfulResponse() throws Exception {
mockMvc.perform(get("/books?page=2"))
.andExpect(status().isOk())
.andDo(document("books",
requestParameters(parameterWithName("page").description("The page to retrieve"))));
}
4.3.放心
最後,讓我們來看看 REST-assured 的範例.
因為我們需要一個正在運行的伺服器,所以我們不應該使用@WebMvcTest
或@AutoconfigureMockMvc.
在這裡,我們使用@AutoconfigureWebMvc
並提供正確的連接埠:
@BeforeEach
void setUp(RestDocumentationContextProvider restDocumentation, @LocalServerPort int port) {
this.spec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(restDocumentation))
.setPort(port)
.build();
}
然而,測試看起來大致相同:
@Test
@WithMockUser
void givenEndpoint_whenSendGetRequest_thenSuccessfulResponse() {
RestAssured.given(this.spec).filter(document("users", requestParameters(
parameterWithName("page").description("The page to retrieve"))))
.when().get("/books?page=2")
.then().assertThat().statusCode(is(200));
}
5. 產生文檔
然而,目前我們仍然沒有產生的文檔。為了獲得結果,我們需要執行額外的步驟。
5.1.產生的片段
運行測試後,我們可以在目標資料夾中找到生成的片段。但是,我們可以配置輸出目錄來定義儲存片段的不同位置。一般來說,它們看起來像這樣:
[source,bash]
----
$ curl 'http://localhost:8080/books?page=2' -i -X GET
----
同時,我們可以看到有關參數的信息,這些信息儲存在 .adoc 檔案中。
|===
|Parameter|Description
|`+page+`
|The page to retrieve
|===
5.2.文件生成
下一步是為 AsciiDoctor 提供配置,以建立具有更易讀文件的 HTML。 AsciiDoc 是一種簡單但功能強大的標記語言。我們可以將它用於各種目的,例如產生 HTML 和 PDF 或寫書。
因此,當我們想要產生 HTML 文件時,我們需要概述 HTML 的範本:
= Books With Spring REST Docs
How you should interact with our bookstore:
.request
include::{snippets}/books/http-request.adoc[]
.request-parameters
include::{snippets}/books/request-parameters.adoc[]
.response
include::{snippets}/books/http-response.adoc[]
在我們的例子中,我們使用簡單的格式,但可以創建更複雜的自訂格式,該格式將具有吸引力且資訊豐富。 AsciiDoc 的靈活性幫助我們實現了這一點。
5.3.產生的 HTML
正確設定和配置後,我們可以將生成目標附加到 Maven 階段:
<executions>
<execution>
<id>generate-docs</id>
<phase>package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<snippets>${snippetsDirectory}</snippets>
</attributes>
<sourceDirectory>src/docs/asciidocs</sourceDirectory>
<outputDirectory>target/generated-docs</outputDirectory>
</configuration>
</execution>
</executions>
我們可以運行所需的mvn
命令並觸發生成。我們在上一節定義的範本呈現以下 HTML:
我們可以將此流程附加到我們的管道中,並始終擁有相關且正確的文件。另一個好處是這個過程減少了浪費且容易出錯的手動工作。
六,結論
文件是軟體的重要組成部分。開發人員承認這一點,但只有少數人堅持編寫或維護它。 Spring REST Docs 讓我們可以根據程式碼而不是我們對 API 應該做什麼的理解,以最少的努力產生良好的文件。
與往常一樣,本教程中的所有程式碼都可以在 GitHub 上取得。