在前后端分离的项目中,难免会涉及到接口文档的编写维护问题,正好最近的项目就涉及到了这个方面的东西,为此,有必要记录一下如何优雅的完成这一巨坑的填补。
本次使用的工具是 swagger2,swagger-ui,swagger2markup,knife4j,后边将对这些插件做一个简单的介绍。
1 介绍
swagger 是一个 API 接口文档生成工具,官网:https://swagger.io。项目集成 swagger 后,只需要简单的几个注解便可以在编写接口的同时完成文档的编写,配合 swagger-ui,便可以在项目启动后,直接浏览器访问指定地址,一般是:
http://{IP}:{端口}/{项目名}/swagger-ui.html
调试 API。而整合 swagger2markup 后,还可以将 API 直接导出成 html、markdown 等格式的文档。
knife4j,前身的 swagger-bootstrap-ui
,是 Swagger 生成 Api 文档的增强解决方案,具体介绍这里不再细说,直接上官网:https://doc.xiaominfo.com 。集成这个插件,可以换掉 swagger-ui 丑陋的皮肤,还能完成 API 的排序,同时也能直接生成 markdown 格式的文档。话不多说,开搞!
2 新建项目
2.1 创建
这里创建一个 springboot 项目,几个简单的配置项以后,便是等待下载完成。
2.2 controller
新建一个 controller 包,并创建一个 TestController
@RestController
@RequestMapping("/v1")
public class TestController {
@RequestMapping(value = "/test1", method = RequestMethod.GET)
public String test1(@RequestParam String username, @RequestParam String password) {
return username + password;
}
}
3 集成 swagger
3.1 添加依赖
项目创建完成后,在 pom.xml 中添加如下的依赖:
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
<id>jcenter-releases</id>
<name>jcenter</name>
<url>http://jcenter.bintray.com</url>
</repository>
</repositories>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- 导出html、markdown等 -->
<dependency>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup</artifactId>
<version>1.3.1</version>
</dependency>
将来导出 html 需要的插件配置
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.6</version>
<configuration>
<sourceDirectory>src/docs/asciidoc/generated</sourceDirectory>
<outputDirectory>src/docs/asciidoc/html</outputDirectory>
<headerFooter>true</headerFooter>
<doctype>book</doctype>
<backend>html</backend>
<sourceHighlighter>coderay</sourceHighlighter>
<attributes>
<!--菜单栏在左边-->
<toc>left</toc>
<!--多标题排列-->
<toclevels>3</toclevels>
<!--自动打数字序号-->
<sectnums>true</sectnums>
</attributes>
</configuration>
</plugin>
</plugins>
3.2 配置 swagger2
新建一个 config 包,并创建一个配置类:SwaggerConfig.java
@Configuration
public class SwaggerConfig {
// 接口大标题
private final String title = "测试API";
// 具体的描述
private final String description = "测试项目API文档";
// 接口版本号
private final String version = "1.0.0";
// 服务说明url,服务条款
private final String termsOfServiceUrl = "http://blog.kangaroohy.top";
// licence,许可证
private final String license = "MIT";
// licnce url,许可网址
private final String licenseUrl = "https://mit-license.org/";
// 接口作者联系方式
private final Contact contact = new Contact("kangaroo1122", "http://blog.kangaroohy.top", "326170945@qq.com");
@Bean
public Docket buildDocket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInf())
.groupName("v1-version-api")
.select()
.apis(RequestHandlerSelectors.basePackage("com.kangaroohy.swagger.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo buildApiInf() {
return new ApiInfoBuilder().title(title).termsOfServiceUrl(termsOfServiceUrl).description(description)
.version(version).license(license).licenseUrl(licenseUrl).contact(contact).build();
}
}
同时在项目启动类添加一个注解:@EnableSwagger2
@SpringBootApplication
@EnableSwagger2
public class SwaggerApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerApplication.class, args);
}
}
接着,需要在 controller 中加上 swagger 的注解:
@RestController
@RequestMapping("/v1")
@Api(tags = {"Test接口测试"})
public class TestController {
@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ApiOperation(value = "测试url-test1", httpMethod = "GET", produces = "application/json", notes = "用于获取测试数据")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "姓名", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "String", paramType = "query")
})
public String test1(@RequestParam String username, @RequestParam String password) {
return username + password;
}
}
各个注解在这里做一个简单的介绍。
3.2.1 @Api
注解
说明每个 controller 层控制类的作用,即每个请求类
属性:tags:描述该类的作用
3.2.2 @ApiOperation
注解
作用在控制类中的每个方法上,说明该类的作用
属性:value:说明该类的作用
3.2.3 @ApiParam
注解
@ApiParam 作用于请求方法上,定义 api 参数的注解,属性有:
name:api 参数的英文名
value:api 参数的描述
required:true 表示参数必输,false 标识参数非必输,默认为非必输
此注解通常与 @RequestParam 或者 @PathVariable 集合使用,因为它的作用只是定义每个参数(因此可以不使用,但是为了方便测试,加上效果更好),如果要获取前端的参数还需要通过 @RequestParam 或者 @PathVariable 来获取
3.2.4 @ApiImplicitParams
注解和 @ApiImplicitParam
注解
定义参数的注解除了 @ApiParam 之外,这两个注解也可以定义参数
①、@ApiImplicitParams 定义一组参数
②、@ApiImplicitParam 写在 @ApiImplicitParams 中,定义每个参数的信息,属性为:
name:参数英文名称
value:参数中文名称
paramType:调用的 url 参数形式,“query”为问号"?"后面的拼接参数,“path”为绑定的参数
此时,项目已经大概集成完毕,运行项目,看看效果: http://localhost:8080/swagger-ui.html
点开相应的接口,可以做相应的测试。
http://localhost:8080/v2/api-docs?group=v1-version-api 效果如下:
{
"swagger": "2.0",
"info": {
"description": "测试项目API文档",
"version": "1.0.0",
"title": "测试API",
"termsOfService": "http://blog.kangaroohy.top",
"contact": {
"name": "kangaroo1122",
"url": "http://blog.kangaroohy.top",
"email": "326170945@qq.com"
},
"license": {
"name": "MIT",
"url": "https://mit-license.org/"
}
},
"host": "localhost:8080",
"basePath": "/",
"tags": [
{
"name": "Test接口",
"description": "Test Controller"
}
],
"paths": {
"/v1/test1": {
"get": {
"tags": [
"Test接口"
],
"summary": "测试url-test1",
"description": "用于获取测试数据",
"operationId": "test1UsingGET",
"produces": [
"*/*",
"application/json"
],
"parameters": [
{
"name": "password",
"in": "query",
"description": "密码",
"required": true,
"type": "string"
},
{
"name": "username",
"in": "query",
"description": "姓名",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Forbidden"
},
"404": {
"description": "Not Found"
}
},
"deprecated": false,
"x-order": "1"
}
}
}
}
3.3 配置 swagger2markup
需要添加的依赖和插件在 3.1 已经添加好了,接下来是新建一个导出文档的配置类:ExportConfig.java
@RunWith(SpringRunner.class)
public class ExportConfig {
/**
* 生成AsciiDocs格式文档
* @throws Exception
*/
@Test
public void generateAsciiDocs() throws Exception {
// 输出Ascii格式
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs?group=v1-version-api"))
.withConfig(config)
.build()
.toFolder(Paths.get("src/docs/asciidoc/generated"));
}
/**
* 生成Markdown格式文档
* @throws Exception
*/
@Test
public void generateMarkdownDocs() throws Exception {
// 输出Markdown格式
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.MARKDOWN)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs?group=v1-version-api"))
.withConfig(config)
.build()
.toFolder(Paths.get("src/docs/markdown/generated"));
}
/**
* 生成Confluence格式文档
* @throws Exception
*/
@Test
public void generateConfluenceDocs() throws Exception {
// 输出Confluence使用的格式
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.CONFLUENCE_MARKUP)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs?group=v1-version-api"))
.withConfig(config)
.build()
.toFolder(Paths.get("src/docs/confluence/generated"));
}
/**
* 生成AsciiDocs格式文档,并汇总成一个文件
* @throws Exception
*/
@Test
public void generateAsciiDocsToFile() throws Exception {
// 输出Ascii到单文件
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs?group=v1-version-api"))
.withConfig(config)
.build()
.toFile(Paths.get("src/docs/asciidoc/generated/all"));
}
/**
* 生成Markdown格式文档,并汇总成一个文件
* @throws Exception
*/
@Test
public void generateMarkdownDocsToFile() throws Exception {
// 输出Markdown到单文件
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.MARKDOWN)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs?group=v1-version-api"))
.withConfig(config)
.build()
.toFile(Paths.get("src/docs/markdown/generated/all"));
}
}
在 idea 中配置一个 maven 命令 asciidoctor:process-asciidoc
,方便执行(也可不用配置,每次需要导出时手动输入运行)
3.4 导出 html 和 markdown 文档
首先运行 ExportConfig.java 中的 generateAsciiDocsToFile()
方法,将文档生成到一个总的 AsciiDocs 格式文档,再执行之前配置的 maven 命令,生成 html 文档,完成之后,打开项目下 docs 文件,便可以看到生成的文档。
同理,运行 generateMarkdownDocsToFile()
将生成一个总的 markdown 格式的文档,其他的可以自测。
4 集成 knife4j
虽然 swagger-ui 能完成前端文档的展示和调试,但是界面不是很友好,为此,引入 knife4j,优点就不说了,具体看官方文档。
4.1 依赖
删除官方的 swagger-ui
,并引入 knife4j,其他的依赖不变
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>1.9.6</version>
</dependency>
4.2 配置
在 swagger 的配置类 SwaggerConfig.java 上加一个注解:@EnableSwaggerBootstrapUi
,值得注意的是,这个注解的最后一个字母是小写 i,如果改成大写 I,则只是给前端文档换一个皮肤,不能使用增强功能(排序 等)
此时,访问 http://{IP}:{端口}/{项目名}/doc.html
,即:http://localhost:8080/doc.html(我这里 IEAD 运行项目时没有添加项目名,这个根据自己设定来。)效果如下:
4.3 其他设置
项目接口排序,有两种排序的方式,具体操作,官方说的很清楚,直接上链接:接口排序
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于