本文介绍一个很是好用的自动化生成 Restful API 文档的工具——Api2Doc 它基于 SpringBoot ,原理相似于 Swagger2,但比 Swagger2 要简单好用。html
此项目已经放到 github 中,须要源码的朋友请点击 这里前端
新增了如下功能:java
@GetMapping
、 @PostMapping
、 @PutMapping
、 @DeleteMapping
、 @PatchMapping
; (以前只支持 @RequestMapping
。)@PathVariable
、 @RequestHeader
、 @CookieValue
、 @RequestPart
; (以前只支持 @RequestParam
。) 并在文档页面的“请求参数”表格,加上“参数形式”这一列。修复了如下 BUG:git
欢迎你们使用!github
在互联网/移动互联网软件的研发过程当中,大多数研发团队先后台分工是很是明确的, 后台工程师负责服务端系统的开发,通常是提供 HTTP/HTTPS 的 Restful API 接口, 前端工程师则负责 Android、iOS、H5页面的开发,须要调用 Restful API 接口。web
这就须要有一套 Restful API 文档,以帮助两方在 API 接口进行沟通,并达成一致意见。 通常状况下,编写文档的工做都会落在后台工程师身上,毕竟 API 是他们提供的嘛。spring
但问题是,编写 Restful API 文档是一件既繁琐、又费时、还对提升技术能力没啥帮助的苦差事, 尤为在是快速迭代、需求频繁修改的项目中,改了代码还要同步改文档, 哪点改错了或改漏了均可能产生先后端实现的不一致,致使联调时发现 BUG, 这个锅最终仍是要后台工程师来背(宝宝内心苦啊...)。后端
所以,业界就出现了一些根据代码自动生成 Restful API 文档的开源项目, 与 Spring Boot 结合比较好的是 Swagger2,Swagger2 经过读取 Controller 代码中的注解信息,来自动生成 API 文档,能够节省大量的手工编写文档的工做量。api
本项目做者以前也是用的 Swagger2,但发现 Swagger2 也有好多地方用得不爽:网络
第一,Swagger2 的注解很是臃肿,咱们看下这段代码:
@RestController @RequestMapping(value = "/user3") public class UserController2Swagger2 { @ApiOperation(value = "获取指定id用户详细信息", notes = "根据user的id来获取用户详细信息", httpMethod = "GET") @ApiImplicitParams({ @ApiImplicitParam(name = "userName", value = "用户名", paramType = "query", required = true, dataType = "String"), @ApiImplicitParam(name = "password", value = "用户密码", paramType = "query", required = true, dataType = "String") }) @RequestMapping(name = "用户注册", value = "/regist", method = RequestMethod.GET) public UserInfo regist(@RequestParam("userName") String userName, @RequestParam("password") String password) { return new UserInfo(); } }
@ApiOperation、@ApiImplicitParam 都是 Swagger2 提供的注解,用于定义 API 信息。 其实,API 方法自己就包含了不少信息,如HTTP Method、参数名、参数类型等等, 像 @ApiImplicitParam 中除了 value 属性有用外,其它都是重复描述。
第二,Swagger2 的页面排版不太友好,它是一个垂直排列的方式,不利于信息的展现。 而且看 API 详细信息还要一个个展开,中间还夹杂着测试的功能,反正做为文档是不易于阅读; 至于做为测试工具嘛...,如今专业的测试工具也有不少,测试人员好像也不选它。
第三,Swagger2 还有好多细节没作好,好比看这个图:
红框中的 API 其实对应的是同一个方法,之因此有这么多,只是由于写这个方法 时没有指定 method:
@RestController @RequestMapping(value = "/user2") public class UserController2Swagger2 { @RequestMapping(value = "/do_something") public void doSomethingRequiredLogon() { } // 其它方法,这里省略... }
(当没指定 method 时,Spring Boot 会默认让这个接口支持全部的 method)
所以,考虑到与其长长久久忍受 Swagger2 的各类不爽,不如花些时间作一个 更好用的“自动化文档系统”,因而就诞生了本项目: Api2Doc 。
Api2Doc 专一于 Restful API 文档的自动生成,它的原理与 Swagger2 是相似的, 都是经过反射,分析 Controller 中的信息生成文档,但它要比 Swagger2 好不少。
最大的不一样是: Api2Doc 比 Swagger2 要少写不少代码。
举个例子,使用 Swagger2 的代码是这样的:
@RestController @RequestMapping(value = "/user") public class UserController { @ApiOperation(value = "添加用户", httpMethod = "POST", notes = "向用户组中添加用户,能够指定用户的类型") @ApiImplicitParams({ @ApiImplicitParam(name = "group", value = "用户组名", paramType = "query", required = true, dataType = "String"), @ApiImplicitParam(name = "name", value = "用户名", paramType = "query", required = true, dataType = "String"), @ApiImplicitParam(name = "type", value = "用户类型", paramType = "query", required = true, dataType = "String") }) @RequestMapping(value = "/addUser", method = RequestMethod.POST) public User addUser(String group, String name, String type) { return null; // TODO: 还未实现。 } }
咱们看下使用 Api2Doc 注解修饰后的代码:
@Api2Doc(id = "users") @ApiComment(seeClass = User.class) @RestController @RequestMapping(value = "/api2doc/demo2") public class UserController2 { @ApiComment("向用户组中添加用户,能够指定用户的类型") @RequestMapping(name = "添加用户", value = "/user", method = RequestMethod.POST) public User addUser(String group, String name, String type) { return null; // TODO: 还未实现。 } // 其它方法,这里省略... }
看,Api2Doc 仅须要在方法上加上 @Api2Doc @ApiComment 注解等极少数代码, 但它生成的文档可一点不含糊,以下图所示:
有的朋友可能会以为很奇怪:文档页面上的说明、示例值等内容,在代码中没有写啊, 这些是哪来的呢?
这里涉及到 Api2Doc 的核心设计理念,就是:它尽量经过智能分析,自动收集 生成文档所需的信息,从而让用户少写代码。
说得有点抽象哈,下面咱们来正面回答这个问题,请你们注意这个类上有一个注解:
@ApiComment(seeClass = User.class)
它意思是: 在 API 方法上遇到没写说明信息时,请参照 User 类中的定义的说明信息。
下面是 User 类的代码:
public class User { @ApiComment(value = "用户id", sample = "123") private Long id; @ApiComment(value = "用户名", sample = "terran4j") private String name; @ApiComment(value = "帐号密码", sample = "sdfi23skvs") private String password; @ApiComment(value = "用户所在的组", sample = "研发组") private String group; @ApiComment(value = "用户类型", sample = "admin") private UserType type; @ApiComment(value = "是否已删除", sample = "true") @RestPackIgnore private Boolean deleted; @ApiComment(value = "建立时间\n也是注册时间。") private Date createTime; // 省略 getter / setter 方法。 }
你们看明白了没? API 方法中的参数,若是与 User 类的属性同名的话,就用类 属性的 @ApiComment 说明信息自动填充。
其实这也符合实际的业务逻辑。由于在大部分项目中,有的字段会在多个实体类、 多个 API 方法中用到,彻底没有必要重复编写其说明信息,只要有一个地方定义好了, 而后其它地方参照就好了。
固然,这只是 Api2Doc 比 Swagger2 好用的特性之一,还有很多比 Swagger2 好用的地方。
下面咱们就来全面讲解它的用法,但愿能够帮助开发者们从文档编写的苦海中解脱出来。
若是是 maven ,请在 pom.xml 中添加依赖,以下所示:
<dependency> <groupId>com.github.terran4j</groupId> <artifactId>terran4j-commons-api2doc</artifactId> <version>${api2doc.version}</version> </dependency>
若是是 gradle,请在 build.gradle 中添加依赖,以下所示:
compile "com.github.terran4j:terran4j-commons-api2doc:${api2doc.version}"
${api2doc.version} 最新稳定版,请参考 这里
本教程的示例代码在 src/test/java 目录的 com.terran4j.demo.api2doc 中, 您也能够从 这里 获取到。
首先,咱们须要在有 @SpringBootApplication 注解的类上,添加 @EnableApi2Doc 注解,以启用 Api2Doc 服务,以下代码所示:
package com.terran4j.demo.api2doc; import com.terran4j.commons.api2doc.config.EnableApi2Doc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // 文档访问地址: http://localhost:8080/api2doc/home.html @EnableApi2Doc @SpringBootApplication public class Api2DocDemoApp { public static void main(String[] args) { SpringApplication.run(Api2DocDemoApp.class, args); } }
而后咱们在 RestController 类添加 @Api2Doc 注解,在须要有文档说明的地方 添加 @ApiComment 注解便可,以下所示:
package com.terran4j.demo.api2doc; import com.terran4j.commons.api2doc.annotations.Api2Doc; import com.terran4j.commons.api2doc.annotations.ApiComment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @Api2Doc(id = "demo1", name = "用户接口1") @ApiComment(seeClass = User.class) @RestController @RequestMapping(value = "/api2doc/demo1") public class UserController1 { @ApiComment("添加一个新的用户。") @RequestMapping(name = "新增用户", value = "/user", method = RequestMethod.POST) public User addUser(String group, String name, @ApiComment("用户类型") UserType type) { return null; // TODO: 还未实现。 } }
这个方法的返回类型 User 类的定义为:
public class User { @ApiComment(value = "用户id", sample = "123") private Long id; @ApiComment(value = "用户名", sample = "terran4j") private String name; @ApiComment(value = "帐号密码", sample = "sdfi23skvs") private String password; @ApiComment(value = "用户所在的组", sample = "研发组") private String group; @ApiComment(value = "用户类型", sample = "admin") private UserType type; @ApiComment(value = "是否已删除", sample = "true") @RestPackIgnore private Boolean deleted; @ApiComment(value = "建立时间\n也是注册时间。") private Date createTime; // 省略 getter / setter 方法。 }
以及 type 属性的类型,也就是 UserType 类的定义为:
package com.terran4j.demo.api2doc; import com.terran4j.commons.api2doc.annotations.ApiComment; public enum UserType { @ApiComment("管理员") admin, @ApiComment("普通用户") user }
编写好代码后,咱们运行 main 函数,访问 Api2Doc 的主页面:
http://localhost:8080/api2doc/home.html
文档页面以下:
说明 Api2Doc 服务起做用了,就是这么简单!
Api2Doc 一共有 3 个注解:@Api2Doc、@ApiComment 及 @ApiError 。
@Api2Doc 用于对文档的生成进行控制。
@Api2Doc 修饰在类上,表示这个类会参与到文档生成过程当中,Api2Doc 服务 会扫描 Spring 容器中全部的 Controller 类,只有类上有 @Api2Doc 的类, 才会被生成文档,一个类对应于文档页面左侧的一级菜单项,@Api2Doc 的 name 属性则表示这个菜单项的名称。
@Api2Doc 也能够修饰在方法,不过在方法上的 @Api2Doc 一般是能够省略, Api2Doc 服务会扫描这个类的全部带有 @RequestMapping 的方法, 每一个这样的方法对应文档页面的左侧的二级菜单项, 菜单项的名称取 @RequestMapping 的 name 属性,固然您仍然能够在方法上用 @Api2Doc 的 name 属性进行重定义。
@ApiComment 用于对 API 进行说明,它能够修饰在不少地方:
若是相同名称、相赞成义的属性或参数字段,其说明已经在别的地方定义过了, 能够用 @ApiComment 的 seeClass 属性表示采用指定类的同名字段上的说明信息, 因此如这段代码:
@Api2Doc(id = "demo1", name = "用户接口1") @ApiComment(seeClass = User.class) @RestController @RequestMapping(value = "/api2doc/demo1") public class UserController1 { @ApiComment("添加一个新的用户。") @RequestMapping(name = "新增用户", value = "/user", method = RequestMethod.POST) public User addUser(String group, String name, UserType type) { return null; // TODO: 还未实现。 } }
虽然 group, name ,type 三个参数没有用 @ApiComment 进行说明, 但因为这个类上有 @ApiComment(seeClass = User.class) , 所以只要 User 类中有 group, name ,type 字段而且有 @ApiComment 的说明就好了。
@ApiError 用于定义错误码,有的 API 方法在执行业务逻辑时会产生错误, 出错后会在返回报文包含错误码,以方便客户端根据错误码做进一步的处理, 所以也须要在 API 文档上体现错误码的说明。
以下代码演示了 @ApiError 的用法:
@Api2Doc(id = "demo", name = "用户接口", order = 0) @ApiComment(seeClass = User.class) @RestController @RequestMapping(value = "/src/test/resources/demo") public class UserController { @Api2Doc(order = 50) @ApiComment("根据用户id,删除指定的用户") @ApiError(value = "user.not.found", comment = "此用户不存在!") @ApiError(value = "admin.cant.delete", comment = "不容许删除管理员用户!") @RequestMapping(name = "删除指定用户", value = "/user/{id}", method = RequestMethod.DELETE) public void delete(@PathVariable("id") Long id) { } }
@ApiError 的 value 属性表示错误码,comment 表示错误码的说明。
错误码信息会显示在文档的最后面,效果以下所示:
咱们能够用 @Api2Doc 中的 order 属性给菜单项排序,order 的值越小, 该菜单项就越排在前面,好比对于这段代码:
package com.terran4j.demo.api2doc; import com.terran4j.commons.api2doc.annotations.Api2Doc; import com.terran4j.commons.api2doc.annotations.ApiComment; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.List; @Api2Doc(id = "demo2", name = "用户接口2", order = 1) @ApiComment(seeClass = User.class) @RestController @RequestMapping(value = "/api2doc/demo2") public class UserController2 { @Api2Doc(order = 10) @ApiComment("添加一个新的用户。") @RequestMapping(name = "新增用户", value = "/user", method = RequestMethod.POST) public User addUser( @ApiComment("用户组名称") String group, @ApiComment("用户名称") String name, @ApiComment("用户类型") UserType type) { return null; // TODO: 还未实现。 } @Api2Doc(order = 20) @ApiComment("根据用户id,查询此用户的信息") @RequestMapping(name = "查询单个用户", value = "/user/{id}", method = RequestMethod.GET) public User getUser(@PathVariable("id") Long id) { return null; // TODO: 还未实现。 } @Api2Doc(order = 30) @ApiComment("查询全部用户,按注册时间进行排序。") @RequestMapping(name = "查询用户列表", value = "/users", method = RequestMethod.GET) public List<User> getUsers() { return null; // TODO: 还未实现。 } @Api2Doc(order = 40) @ApiComment("根据指定的组名称,查询该组中的全部用户信息。") @RequestMapping(name = "查询用户组", value = "/group/{group}", method = RequestMethod.GET) public UserGroup getGroup(@PathVariable("group") String group) { return null; // TODO: 还未实现。 } }
显示的结果为:
在类上的 @Api2Doc 一样能够给一级菜单排序,规则是同样的,这里就不演示了。
有时候光有自动生成的 API 文档彷佛还不太完美,或许咱们想补充点别的什么东西, 好比: 对项目的背景介绍、技术架构说明之类,那这个要怎么弄呢?
Api2Doc 容许用 md 语法手工编写文档,并集成到自动生成的 API 文档之中,方法以下:
首先,要在类上的 @Api2Doc 定义 id 属性,好比对下面这个类:
package com.terran4j.demo.api2doc; import com.terran4j.commons.api2doc.annotations.Api2Doc; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Api2Doc(id = "demo3", name = "用户接口3") @RestController @RequestMapping(value = "/api2doc/demo3") public class UserController3 { @Api2Doc(order = 10) @RequestMapping(name = "接口1", value = "/m1") public void m1() { } @Api2Doc(order = 20) @RequestMapping(name = "接口2", value = "/m2") public void m2() { } }
@Api2Doc(id = "demo3", name = "用户接口3") 表示:对应的一级菜单“用户接口3” 的 id 为 demo3。
而后,咱们在 src/main/resources 中建立目录 api2doc/demo3, 前面的 api2doc 是固定的,后面的 demo3 表示这个目录中的文档是添加到 id 为 demo3 的一级文档菜单下。
而后咱们在 api2doc/demo3 目录中编写 md 格式的文档,以下图所示:
文件名的格式为 ${order}-${文档名称}.md,即 - 号前面的数字表示这个文档的排序, 与 @Api2Doc 中的 order 属性是同样的,而 - 号后面是文档名称,也就是二级菜单的名称。
所以,最后文档的显示效果为:
看,手工编写的补充文档与自动生成的 API 文档,经过 order 进行排序组合在一块儿, 看起来毫无违和感。
每次访问文档页面 http://localhost:8080/api2doc/home.html 时, 中间的内容是很是简单的一句:
欢迎使用 Api2Doc !
这彷佛有点不太好,不过不要紧,咱们能够编写本身的欢迎页。
方法很简单,在 src/main/resources 目录的 api2doc 目录下,建立一个名为 welcome.md 的文件(这个名称是固定的),而后用 md 语法编写内容就能够。
能够在 application.yml 中配置文档的标题及图标,以下所示:
api2doc: title: Api2Doc示例项目——接口文档 icon: https://spring.io/img/homepage/icon-spring-framework.svg
图标为一个全路径 URL,或本站点相对路径 URL 都行。
配置后的显示效果为:
您在 application.yml 中配置 api2doc.enabled 属性,以开启或关闭 Api2Doc 服务,如:
# 本地环境 api2doc: title: Api2Doc示例项目——接口文档 icon: https://spring.io/img/homepage/icon-spring-framework.svg --- # 线上环境 spring: profiles: online api2doc: enabled: false
api2doc.enabled 为 false 表示关闭 Api2Doc 服务,不写或为 true 表示启用。
因为 Api2Doc 服务没有访问权限校验,建议您在受信任的网络环境(如公司内网) 中才启用 Api2Doc 服务。