通过swagger2与swagger-ui可以很方便的生成系统的在线api文档,这方面的博客网上有很多。
但是利用swagger生成离线api文档的博客就不多了。有的无法兼容springboot2,有的配置起来太麻烦,复用性与易用性较差。
为了能够方便的自动生成api离线文档,笔者花了些时间基于网上的博客做了修改,代码经过亲测可用于springboot2项目。
代码样例下载地址
Github: https://github.com/ChaselX/springboot-swagger2-offline-api-doc
Gitee: https://gitee.com/chasel96/springboot-swagger2-offline-api-doc
旧版与新版的区别
个人觉得旧版的配置简单许多,新版的配置按照官方demo的配置来做还是复杂了很多
重要提示1: 本文档有两种版本生成,上面的只能基于2.6.1以下的swagger版本使用,下面的可以用于2.6.1以上版本。笔者在官档上看到了使用最新的swagger来生成离线api文档的方法,并已经在代码样例中实现了。
重要提示2:在生成pdf离线文档时,因为adoc的字符编码问题,部分中文会消失,网上有现成的解决办法,但是由于解决的方式不够灵活,不在文档中展示。生成html离线文档不受影响。
使用2.6.1及以下版本生成Api文档
离线文档生成效果预览
最终目标
配置到Springboot项目中以后,在项目打包的时候便会通过单元测试在指定的目录生成被官方称为staticdocs的离线文档
从Maven配置开始
Maven依赖引入
该篇博文引用的依赖都要引入,Spring Rest Docs的依赖spring-restdocs-mockmvc,离线文档的依赖springfox-staticdocs,因为要在单元测试的时候生成文档,所以需要再加测试相关的spring-boot-starter-test。
注意:因为springfox-staticdocs在16年以后就没有发布新依赖包了,swagger2与swagger-ui的依赖包版本必须为2.6.1,否则会因为springfox-staticdocs的swagger-model与swagger2中的model不兼容导致错误。亲测踩坑填坑。
UTF-8
UTF-8
1.8
${project.build.directory}/generated-snippets
org.springframework.boot
spring-boot-starter-web
io.springfox
springfox-swagger2
2.6.1
io.springfox
springfox-swagger-ui
2.6.1
org.springframework.restdocs
spring-restdocs-mockmvc
2.0.2.RELEASE
test
io.springfox
springfox-staticdocs
2.6.1
test
org.springframework.boot
spring-boot-starter-test
test
Maven插件
asciidoctor-maven-plugin
插件会把Asciidoc格式文件转成HTML5格式输出。
org.springframework.boot
spring-boot-maven-plugin
org.apache.maven.plugins
maven-surefire-plugin
**/*Documentation.java
org.asciidoctor
asciidoctor-maven-plugin
1.5.3
${project.basedir}/docs/asciidoc
index.adoc
book
left
3
${project.build.directory}/asciidoc
output-html
test
process-asciidoc
html5
org.jruby
jruby-complete
1.7.26
生成文档的单元测试代码编写
package com.chinamobile.cmic.apidoc;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.github.robwin.swagger2markup.GroupBy;
import io.github.robwin.swagger2markup.Swagger2MarkupConverter;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.operation.preprocess.Preprocessors;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.web.context.WebApplicationContext;
import springfox.documentation.staticdocs.SwaggerResultHandler;
/**
* @author ChaselX
* @date 2019/1/29 13:02
*/
@AutoConfigureMockMvc
@AutoConfigureRestDocs(outputDir = "target/generated-snippets")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Documentation {
private String snippetDir = "target/generated-snippets";
private String outputDir = "target/asciidoc";
@Autowired
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@After
public void test() throws Exception {
// 得到swagger.json,写入outputDir目录中
mockMvc.perform(RestDocumentationRequestBuilders.get("/v2/api-docs")
.accept(MediaType.APPLICATION_JSON))
.andDo(SwaggerResultHandler.outputDirectory(outputDir).build())
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
// 读取上一步生成的swagger.json转成asciiDoc,写入到outputDir
// 这个outputDir必须和插件里面 标签配置一致
Swagger2MarkupConverter.from(outputDir + "/swagger.json")
.withPathsGroupedBy(GroupBy.TAGS)// 按tag排序
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)// 格式
.withExamples(snippetDir)
.build()
.intoFolder(outputDir);// 输出
}
@Test
public void testApi() {
try {
mockMvc.perform(RestDocumentationRequestBuilders.get("/greeting")
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcRestDocumentation.document("greeting",
Preprocessors.preprocessResponse(Preprocessors.prettyPrint())))
.andReturn();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
}
}
这个类包含两个方法,TestApi()是用来生成例子,test()用来生成Asciidoc的文档。生成例子用到了spring-restdocs-mockmvc,每一个API都要进行单元测试才能生成相应的文档片段(snippets),生成的结果如图:
生成完整的Asciidoc文档用到了Swagger2MarkupConverter
,第一步先获取在线版本的文档并保存到文件swagger.json
中,第二步把swagger.json
和之前的例子snippets整合并保存为Asciidoc格式的完整文档。生成结果如图:
Swagger配置类
通过配置类定义一些文档相关的信息
package com.chinamobile.cmic.apidoc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author ChaselX
* @date 2019/1/30 11:22
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.chinamobile.cmic.apidoc"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("API Document")
.description("API Document for Spring Boot 2 Project")
.contact(new Contact("ChaselX", "", ""))
.version("1.0")
.build();
}
}
index.adoc
路径:项目名/docs/asciidoc/index.adoc
include::{generated}/overview.adoc[]
include::{generated}/definitions.adoc[]
include::{generated}/paths.adoc[]
生成HTML5的文档
利用前面配置的maven插件,只需要执行打包就可以生成相应的文档,如图:
参考资料
- https://www.jianshu.com/p/af7a6f29bf4f
- https://blog.csdn.net/fly910905/article/details/79131755
使用2.6.1及以上版本生成Api文档
从Maven配置开始
Maven依赖引入
该篇博文引用的依赖都要引入,Spring Rest Docs的依赖spring-restdocs-mockmvc,离线文档的依赖springfox-staticdocs,因为要在单元测试的时候生成文档,所以需要再加测试相关的spring-boot-starter-test。
UTF-8
UTF-8
1.8
1.2.0
${project.basedir}/src/docs/asciidoc
${project.build.directory}/swagger
${project.build.directory}/asciidoc/snippets
${project.build.directory}/asciidoc/generated
${project.build.directory}/asciidoc/html
${project.build.directory}/asciidoc/pdf
${swagger.output.dir}/swagger.json
2.9.2
jcentral
bintray
http://jcenter.bintray.com
false
jcenter-snapshots
jcenter
http://oss.jfrog.org/artifactory/oss-snapshot-local/
org.springframework.boot
spring-boot-starter-web
net.logstash.logback
logstash-logback-encoder
5.1
io.springfox
springfox-swagger2
${springfox.version}
io.springfox
springfox-swagger-ui
${springfox.version}
org.springframework.restdocs
spring-restdocs-mockmvc
io.github.swagger2markup
swagger2markup-spring-restdocs-ext
${swagger2markup.version}
test
io.springfox
springfox-staticdocs
2.6.1
org.springframework.boot
spring-boot-starter-test
test
Maven插件
asciidoctor-maven-plugin
插件会把Asciidoc格式文件转成HTML5格式输出。
jcenter-snapshots
jcenter
http://oss.jfrog.org/artifactory/oss-snapshot-local/
jcenter-releases
jcenter
http://jcenter.bintray.com
false
org.springframework.boot
spring-boot-maven-plugin
org.apache.maven.plugins
maven-compiler-plugin
3.3
${java.version}
${java.version}
${java.version}
UTF-8
false
org.apache.maven.plugins
maven-surefire-plugin
${swagger.output.dir}
${swagger.snippetOutput.dir}
io.github.swagger2markup
swagger2markup-maven-plugin
${swagger2markup.version}
io.github.swagger2markup
swagger2markup-import-files-ext
${swagger2markup.version}
io.github.swagger2markup
swagger2markup-spring-restdocs-ext
${swagger2markup.version}
${swagger.input}
${generated.asciidoc.directory}
ASCIIDOC
TAGS
${project.basedir}/src/docs/asciidoc/extensions/overview
${project.basedir}/src/docs/asciidoc/extensions/definitions
${project.basedir}/src/docs/asciidoc/extensions/paths
${project.basedir}src/docs/asciidoc/extensions/security/
${swagger.snippetOutput.dir}
true
test
convertSwagger2markup
org.asciidoctor
asciidoctor-maven-plugin
1.5.6
org.asciidoctor
asciidoctorj-pdf
1.5.0-alpha.16
org.jruby
jruby-complete
1.7.21
${asciidoctor.input.directory}
index.adoc
book
left
3
${generated.asciidoc.directory}
output-html
test
process-asciidoc
html5
${asciidoctor.html.output.directory}
output-pdf
test
process-asciidoc
pdf
${asciidoctor.pdf.output.directory}
org.apache.maven.plugins
maven-jar-plugin
3.1.0
true
lib/
com.chinamobile.cmic.apidoc.ApiDocApplication
maven-dependency-plugin
package
copy-dependencies
${project.build.directory}/lib
maven-resources-plugin
3.1.0
copy-resources
prepare-package
copy-resources
${project.build.outputDirectory}/static/docs
${asciidoctor.html.output.directory}
${asciidoctor.pdf.output.directory}
生成文档的单元测试代码编写
package com.chinamobile.cmic.apidoc;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author ChaselX
* @date 2019/1/29 13:02
*/
@WebAppConfiguration
@RunWith(SpringRunner.class)
@AutoConfigureRestDocs(outputDir = "build/asciidoc/snippets")
@SpringBootTest
@AutoConfigureMockMvc
public class Swagger2MarkupTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testApi() throws Exception {
mockMvc.perform(get("/greeting")
.accept(MediaType.APPLICATION_JSON))
.andDo(document("greetingGet",
Preprocessors.preprocessResponse(Preprocessors.prettyPrint())))
.andExpect(status().isOk());
}
@Test
public void createSpringfoxSwaggerJson() throws Exception {
String outputDir = System.getProperty("io.springfox.staticdocs.outputDir");
MvcResult mvcResult = this.mockMvc.perform(get("/v2/api-docs")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
String swaggerJson = response.getContentAsString();
Files.createDirectories(Paths.get(outputDir));
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputDir, "swagger.json"), StandardCharsets.UTF_8)) {
writer.write(swaggerJson);
}
}
}
这个类包含两个方法,TestApi()是用来生成例子,createSpringfoxSwaggerJson()用来生成Asciidoc的文档。生成例子用到了spring-restdocs-mockmvc,每一个API都要进行单元测试才能生成相应的文档片段(snippets),生成的结果如图:
生成完整的Asciidoc文档用到了Swagger2MarkupConverter
,第一步先获取在线版本的文档并保存到文件swagger.json
中,第二步把swagger.json
和之前的例子snippets整合并保存为Asciidoc格式的完整文档。生成结果如图:
Swagger配置类
通过配置类定义一些文档相关的信息
package com.chinamobile.cmic.apidoc.config;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import static springfox.documentation.builders.PathSelectors.ant;
/**
* @author ChaselX
* @date 2019/1/30 11:22
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket restApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.paths(Predicates.and(ant("/**"), Predicates.not(ant("/error"))))
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API Document")
.description("API Document for Spring Boot 2 Project")
.contact(new Contact("ChaselX", "", ""))
.version("1.0")
.build();
}
}
LogstashEncoder
在resources目录下创建一个名为logback.xml的配置文件,使用LogstashEncoder作为Default Log Encoder
index.adoc
路径:项目名src/docs/asciidoc/index.adoc
include::{generated}/overview.adoc[]
include::{generated}/paths.adoc[]
include::{generated}/security.adoc[]
include::{generated}/definitions.adoc[]
生成HTML5、PDF的文档
利用前面配置的maven插件,只需要执行打包就可以生成相应的文档,如图: