1. 概述
文档对于任何要共享给世界的代码都至关重要,尤其是当代码相对复杂时。优秀的 API 文档不仅能吸引开发者使用,更能体现产品质量。 如果一家公司的文档写得马马虎虎,其 API 的质量恐怕也堪忧。
但开发者都喜欢写机器能理解的代码,而不是给人看的文字。
本教程将探讨如何结合使用 Spring REST Docs 来同时编写文档和 API。我们将以查询参数文档化为例展开。
2. API 设计
考虑一个简单的单接口 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 Docs 支持主流的 REST 测试框架。 我们将以 MockMvc、WebTestClient 和 REST-assured 为例,但核心思路对所有框架都适用。
我们将使用 JUnit 5 作为测试基础,但也支持 JUnit 4。
所有测试方法都需要添加以下扩展:
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
这些是专门用于生成文档的特殊类。
4.1. WebTestClient
先从现代的 WebTestClient 开始。如前所述,需要扩展测试类并正确配置:
@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
最后看 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. 生成的代码片段
运行测试后,可在 target 目录找到生成的片段。也可配置输出目录指定存储位置。片段内容类似这样:
[source,bash]
----
$ curl 'http://localhost:8080/books?page=2' -i -X GET
----
同时会生成包含参数说明的 .adoc
文件:
|===
|Parameter|Description
|`+page+`
|The page to retrieve
|===
5.2. 文档生成流程
下一步需要配置 AsciiDoc 将片段转换为可读的 HTML。AsciiDoc 是一种简洁强大的标记语言,可用于生成 HTML/PDF 或 写书。
要生成 HTML 文档,需先定义模板:
= 使用 Spring REST Docs 的书籍文档
书店交互指南:
.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:
可将此流程集成到 CI/CD 管道中,确保文档始终最新准确。另一个好处是减少了易出错的手工劳动。
6. 总结
文档是软件的重要组成部分。开发者都明白这一点,但很少有人能坚持编写或维护。Spring REST Docs 让我们能基于代码(而非主观理解)轻松生成高质量文档。
✅ 核心优势:
- 测试驱动生成,确保文档与实现同步
- 支持复杂场景的文档化
- 减少重复劳动,降低维护成本
⚠️ 踩坑提醒:
- 务必正确配置输出目录和 Maven 插件
- 测试失败时不会更新文档,需保证测试可靠性
本教程所有代码可在 GitHub 获取。