导读:
在团队协作的时候许多时候需要用到接口文档,我们通常通过手工编写大量重复格式的文档,让我想起了程序员最讨厌的两件事:没有文档,编写文档。哈哈,如果使用过swagger的朋友应该都很了解它带给我们的便利,如果你还没有使用swagger的话,正好打算编写RESTful API文档,这里有一篇文章Spring Boot中使用Swagger2构建强大的RESTful API文档可以帮助你在较短的时间内构建出一个线上文档,有些时候我们需要生成离线文档有该怎么做呢?带着这个问题我们一起去出发。
关于swagger:
Swagger - 前后端分离后的契约
通过Swagger进行API设计,与Tony Tam的一次对话
一个简短的总结,更好的生成RESTful API文档,提供相应的测试功能,效果图如下:
编写离线文档:
swagger为我们提供了生成在线文档的功能,然而有些时候客户需要的是离线文档的api,有没有什么较好的办法可以通过swagger帮助我们生成离线文档呢?
这就是今天的主角:Springfox和Spring Rest Docs帮我们做的事情
1.预备知识:
建议了解swagger、Asciidoc、asciidoctor-maven-plugin和SpringBoot Testing。对应的资料可自行谷歌。
2.关于Springfox和Spring Rest Docs:
官网是这样描述的Springfox:Automated JSON API documentation for API's built with Spring。我们可以理解为为 基于Spring构建的API自动生成文档。
引入pom依赖:
其实我们的思路就是把swagger在线文档转成staticdocs形式的文档,引入相关的一些依赖 Spring Rest Docs的依赖spring-restdocs-mockmvc,离线文档的依赖springfox-staticdocs,因为要在单元测试的时候生成文档,所以再加测试相关的spring-boot-starter-test。
org.springframework.boot
spring-boot-starter-data-jpa
com.h2database
h2
runtime
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
io.springfox
springfox-swagger2
2.6.1
io.springfox
springfox-swagger-ui
2.6.1
org.springframework.restdocs
spring-restdocs-mockmvc
1.1.2.RELEASE
test
io.springfox
springfox-staticdocs
2.6.1
com.alibaba
fastjson
1.2.8
使用Maven插件:
我们使用asciidoctor-maven-plugin插件将Asciidoc格式转成HTML5格式
了解更多: 使用介绍
org.asciidoctor
asciidoctor-maven-plugin
${asciidoctor.html.output.directory}
index.adoc
book
left
3
${generated.asciidoc.directory}
output-html
test
process-asciidoc
html
${project.build.directory}/generated-snippets
编写测试类生成离线文档:
import cn.sunxyz.domain.UserInfo;
import com.alibaba.fastjson.JSON;
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.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.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import springfox.documentation.staticdocs.SwaggerResultHandler;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@AutoConfigureMockMvc
@AutoConfigureRestDocs(outputDir = "target/generated-snippets")
@RunWith(SpringRunner.class)
@SpringBootTest
public class DocumentationBuild {
private String snippetDir = "target/asciidoc/generated-snippets";
private String outputDir = "target/asciidoc";
@Autowired
private MockMvc mockMvc;
@After
public void Test() throws Exception {
// 得到swagger.json,写入outputDir目录中
mockMvc.perform(get("/v2/api-docs").accept(MediaType.APPLICATION_JSON))
.andDo(SwaggerResultHandler.outputDirectory(outputDir).build())
.andExpect(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() throws Exception {
mockMvc.perform(get("/api/user/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(MockMvcRestDocumentation.document("查询用户", preprocessResponse(prettyPrint())));
UserInfo userInfo = new UserInfo();
userInfo.setName("lisi");
userInfo.setAge(23);
userInfo.setAddress("山东济南");
userInfo.setSex("男");
mockMvc.perform(post("/api/user").contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(userInfo))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful())
.andDo(MockMvcRestDocumentation.document("新增用户", preprocessResponse(prettyPrint())));
}
}
由于前面已经配置了maven的插件,只需要执行测试就可以生成相应的文档, 效果图如下:
补充:
Swagger配置:
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
@Bean
public Docket configSpringfoxDocket_all(ApiInfo apiInfo) {
return new Docket(DocumentationType.SWAGGER_2)
.produces(Sets.newHashSet("application/json"))
.consumes(Sets.newHashSet("application/json"))
.protocols(Sets.newHashSet("http", "https"))
.apiInfo(apiInfo)
.forCodeGeneration(true)
.select().paths(regex("/api.*"))
.build();
}
@Bean
public Docket createUserInfoRestApi(ApiInfo apiInfo) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("user")
.produces(Sets.newHashSet("application/json"))
.consumes(Sets.newHashSet("application/json"))
.protocols(Sets.newHashSet("http", "https"))
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("cn.sunxyz.controller"))
.paths(regex("/api/user.*"))
.build();
}
@Bean
public ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Springfox REST API")
.description("Descriptions.")
.termsOfServiceUrl("http://springfox.io")
.license("Apache License Version 2.0")
.licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
.version("2.0")
.build();
}
}
相关源码已托管github
参考资料:
Spring REST Docs
SpringBoot项目生成RESTfull API的文档
Introduction to Spring REST Docs
asciidoctor-maven-plugin