如何使用Rest Doc为带版本控制的REST API生成文档?
我之前在维护带版本控制的REST API文档时,刚好也是用TestNg+Spring Rest Docs+Asciidoc的组合,踩过几个坑,分享几个实用的解决方案,帮你适配版本化的文档需求:
1. 给每个版本的API操作加版本前缀,隔离Snippets
这是最直接的方式,给不同版本的测试生成的snippets加上版本标识,避免互相覆盖,同时在Asciidoc里分版本章节引用。
比如针对v1和v2的Get People接口:
- 在TestNg测试方法里,给
document()方法的operation ID加上版本前缀,比如v1-get-people和v2-get-people:
@Test public void getPeopleV1Test() throws Exception { mockMvc.perform(get("/api/v1/people") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(document("v1-get-people", requestFields(/* v1 请求字段定义 */), responseFields(/* v1 响应字段定义 */))); } @Test public void getPeopleV2Test() throws Exception { mockMvc.perform(get("/api/v2/people") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(document("v2-get-people", requestFields(/* v2 请求字段定义,比如新增了`department`字段 */), responseFields(/* v2 响应字段定义 */))); }
- 然后在Asciidoc文档里分版本引用:
# API Version 1 === Get People (v1) Get the people registered in v1 API. operation::v1-get-people[snippets='http-request,request-fields,http-response,response-fields,error-codes'] # API Version 2 === Get People (v2) Get the people registered in v2 API (supports department filtering). operation::v2-get-people[snippets='http-request,request-fields,http-response,response-fields,error-codes']
这种方式的好处是逻辑清晰,每个版本的snippets完全隔离,不会互相干扰,适合版本差异较大的场景。
2. 用Asciidoc的条件渲染/文件包含,实现版本切换
如果你的API版本之间大部分内容是复用的,只有少量差异,可以用Asciidoc的条件指令来动态渲染对应版本的内容,减少重复代码。
- 先把每个版本的差异部分单独写成小的Asciidoc片段,比如
v1-get-people-diff.adoc和v2-get-people-diff.adoc。 - 主文档里用
ifdef指令根据构建参数选择加载的版本:
=== Get People Get the people registered. ifdef::api-version=v1[] include::v1-get-people-diff.adoc[] operation::v1-get-people[snippets='http-request,request-fields,http-response,response-fields,error-codes'] endif::api-version=v1[] ifdef::api-version=v2[] include::v2-get-people-diff.adoc[] operation::v2-get-people[snippets='http-request,request-fields,http-response,response-fields,error-codes'] endif::api-version=v2[]
构建的时候,你可以通过传递参数指定版本,比如用Maven的话,在asciidoctor-maven-plugin里配置attributes:
<plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>2.2.6</version> <configuration> <attributes> <api-version>${api.version}</api-version> </attributes> </configuration> </plugin>
然后运行mvn asciidoctor:process-asciidoc -Dapi.version=v2就能生成v2版本的文档。
3. 动态注入版本信息到测试中,批量生成多版本Snippets
如果需要批量生成所有版本的文档,可以在TestNg里通过系统参数或者配置文件注入版本号,动态生成对应版本的operation ID和请求路径。
比如在测试类里设置版本变量:
private String apiVersion; @BeforeMethod public void setup() { // 从系统参数获取版本,默认v1 apiVersion = System.getProperty("api.version", "v1"); this.mockMvc = MockMvcBuilders.webAppContextSetup(context) .apply(documentationConfiguration(restDocumentation)) .build(); } @Test public void getPeopleTest() throws Exception { mockMvc.perform(get("/api/{version}/people", apiVersion) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(document(apiVersion + "-get-people", requestFields(getRequestFieldsForVersion(apiVersion)), responseFields(getResponseFieldsForVersion(apiVersion)))); } // 根据版本返回对应的字段定义 private RequestFieldsSnippet getRequestFieldsForVersion(String version) { if ("v2".equals(version)) { return requestFields( fieldWithPath("name").description("Person's full name"), fieldWithPath("department").description("Person's department (v2 only)") ); } else { return requestFields( fieldWithPath("name").description("Person's full name") ); } }
然后运行测试时指定版本:mvn test -Dapi.version=v2,就能生成v2版本的snippets。重复运行不同版本的测试,就能得到所有版本的snippets,之后在Asciidoc里按版本引用即可。
一些额外的小技巧
- 把通用的字段定义(比如错误码、基础响应字段)抽成Asciidoc的宏或者include文件,比如
common-error-codes.adoc,在各个版本的文档里引用,减少重复。 - 用构建工具(Maven/Gradle)的任务来自动化生成多版本文档,比如创建
generate-docs-v1和generate-docs-v2任务,一键生成所有版本的文档。
内容的提问来源于stack exchange,提问作者Manuelarte
相关产品推荐
相关产品推荐

