项目前后端分离,最初时候由于项目规模较小,功能模块较少,后端开发人员都是通过口头交流告知前端开发人员接口格式和数据的,或者直接叫前端开发人员数据库搞起看数据库,但是公司规模扩大,人员增多,业务频繁变更导致前端开发人员不能及时准确的获取接口文档。对比了几种自动化文档的生成工具, 有的太复杂,有的需要入侵代码(swagger),最后决定使用spring-restdocs,只需要编写测试代码,运行测试案例即可生成文档。
使用idea创建Spring Boot项目,直接开始,文档预览
1、引用spring-restdocs和单元测试依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency>
2、配置build
<build> <plugins> <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>1.5.3</version> <executions> <execution> <id>generate-docs</id> <phase>prepare-package</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <backend>html</backend> <doctype>book</doctype> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-asciidoctor</artifactId> <version>2.0.3.RELEASE</version> </dependency> </dependencies> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>2.7</version> <executions> <execution> <id>copy-resources</id> <phase>prepare-package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory> ${project.build.outputDirectory}/static/docs </outputDirectory> <resources> <resource> <directory> ${project.build.directory}/generated-docs </directory> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
3、创建controller,需要生成接口文档的controller,本教程的所有代码可访问 auto-doc 获取
@RestController @RequestMapping("users") public class UserController { private final UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } /** * 分页查询 * * @param user 查询条件 * @param pageParam 分页参数 * @return */ @GetMapping public Result page(User user, PageParam pageParam) { return ResultBuilder.create().success().data(userService.page(user, pageParam)).build(); } /** * 获取单个用户 * * @param id 用户id * @return 用户 */ @GetMapping("{id:\\d+}") public Result get(@PathVariable Long id) { return ResultBuilder.create().success().data(userService.get(id)).build(); } /** * 保存用户 * * @param user 用户 * @return 保存结果 */ @PostMapping public Result save(@Valid @RequestBody User user) { return ResultBuilder.create().success(userService.save(user)).build(); } /** * 删除用户 * * @param id 用户id * @return 删除结果 */ @DeleteMapping("{id:\\d+}") public Result delete(@PathVariable Long id) { userService.delete(id); return ResultBuilder.create().success().build(); } }
4、编写单元测试代码,运行单元测试将会生成.adoc文件(相当于页面碎片,用于引入,再组合成完整的接口文档HTML)
Get接口测试,获取用户信息
@Test public void testGet() throws Exception { this.mockMvc.perform(RestDocumentationRequestBuilders.get("/users/{id}", 1)) .andDo(print()) .andExpect(status().isOk()) // target/generated-snippets/目录下会生成一个user_get目录,不同的接口需要使用不同的目录 .andDo(document("user_get", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), //Get接口中使用了@PathVariable,故需要用pathParameters pathParameters( parameterWithName("id").description("用户id") ), responseFields( fieldWithPath("status").description("状态码"), fieldWithPath("message").description("状态描述"), fieldWithPath("data.id").description("用户id"), fieldWithPath("data.name").description("姓名"), fieldWithPath("data.username").description("用户名"), fieldWithPath("data.password").description("密码") ) )); }
其余接口请参考源码 auto-doc ,.adoc文件参考表
文件名 | 用途 | 说明 |
curl-request.adoc | 提供curl命令 | |
http-request.adoc | 请求报文 | |
http-response.adoc | 响应报文 | |
request-body.adoc | 请求体 | 提交JSON格式数据接口 |
request-parameters.adoc | 请求参数,表格展示 | 提交普通表单数据接口 |
request-fields.adoc | 请求体中的参数,表格展示 | 提交JSON格式数据接口 |
path-parameters.adoc | 请求路径中参数,表格展示 | 使用@PathVariable接口 |
response-body.adoc | 响应数据 | |
response-fields.adoc | 响应参数,表格展示 |
5、编写asciidoc,asciidoc是一种可以使用普通文本编辑器编写的标记语言类似markdown,使用起来也挺简单的。
在main目录下创建asciidoc文件夹,在asciidoc文件夹下创建index.adoc文件:项目根目录/src/main/asciidoc/index.adoc,需要在index.adoc中引入步骤4中生成的.adoc文件,最终会生成HTML文件
= 接口文档 :doctype: book :icons: font :source-highlighter: highlightjs :author: 刘勋明 :email: xunming.liu@gmail.com :version: 1.0.0 :date: 2020-03-11 :toc: left :toc-title: 功能列表 :toclevels: 4 :sectlinks: == 用户 用户相关接口,调用此系列接口完成对用户的操作 === 分页查询 .请求地址 include::{snippets}\user_page\curl-request.adoc[] .请求参数 include::{snippets}\user_page\request-parameters.adoc[] .响应报文 include::{snippets}\user_page\response-body.adoc[] .响应数据 include::{snippets}\user_page\response-fields.adoc[] === 获取用户信息 .请求地址 include::{snippets}\user_get\curl-request.adoc[] .请求参数 include::{snippets}\user_get\path-parameters.adoc[] .响应报文 include::{snippets}\user_get\response-body.adoc[] .响应数据 include::{snippets}\user_get\response-fields.adoc[] === 保存用户 .请求地址 include::{snippets}\user_save\curl-request.adoc[] .请求报文 include::{snippets}\user_save\request-body.adoc[] .响应报文 include::{snippets}\user_save\response-body.adoc[] .响应数据 include::{snippets}\user_save\response-fields.adoc[] === 删除用户 .请求地址 include::{snippets}\user_delete\curl-request.adoc[] .请求参数 include::{snippets}\user_delete\path-parameters.adoc[] .响应报文 include::{snippets}\user_delete\response-body.adoc[] .响应数据 include::{snippets}\user_delete\response-fields.adoc[]
6、生成文档,需要运行单元测试生成.adoc文件,再命令行运行mvn package生成html格式的接口文档,查看 项目根目录/target/generated-doc/index.html
总的来说还是比较方便的在单元测试的同时即可生成接口文档,无非就是单元测试多了一些代码,还需要注意的就是不同的接口数据格式(content-type)对应的代码是不一样的,对应不同的RequestBuilders:MockMvcRequestBuilders和RestDocumentationRequestBuilders,主要在这里折腾了一下其他都比较顺利。
项目源码https://github.com/benjamin-coding/auto-doc
文档预览https://benjamin-coding.github.io/pages/auto-doc.html