还在那 發表於 2020-1-21 14:25:00

项目工具三:Swagger导出离线版HTML5和PDF格式api文档

<p>之前的文章讲解了swagger2注解的用法以及实例演示,本篇文章介绍一下如何使用swagger2导出离线版的api文档,分为两种格式一个是HTML5一个是PDF。对象属性、接口说明、测试用例都可以导出来方便开发人员很清楚的了解接口!</p>
<h2>相关版本</h2>
<ol class="list-paddingleft-2">
<li>
<p>Springboot版本:1.5.10.RELEASE</p>
</li>
<li>
<p>swagger2版本:2.6.1</p>
</li>
<li>
<p>maven版本:3.2.5</p>
</li>
<li>
<p>JDK版本:8</p>
</li>
<li>
<p>IDEA版本:2017.2.6<br>大家自己测试的时候如果不成功尽量和我版本保持一致。</p>

</li>

</ol>
<h2>index.adoc配置</h2>
<p>&nbsp;</p>
<p><img src="https://img2018.cnblogs.com/common/1505525/202001/1505525-20200121142138453-1397566250.png"></p>
<pre><code>include::{generated}/overview.adoc[]
include::{generated}/definitions.adoc[]
include::{generated}/paths.adoc[]</code></pre>
<h2>pom.xml配置</h2>
<p>pom里面不比之前需要多配置一些东西,这个是大家需要注意的。</p>
<p>properties配置:</p>
<pre><code>&lt;properties&gt;
&nbsp; &nbsp;&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&nbsp; &nbsp;&lt;project.reporting.outputEncoding&gt;UTF-8&lt;/project.reporting.outputEncoding&gt;
&nbsp; &nbsp;&lt;java.version&gt;1.8&lt;/java.version&gt;

&nbsp; &nbsp;&lt;snippetsDirectory&gt;${project.build.directory}/generated-snippets&lt;/snippetsDirectory&gt;
&nbsp; &nbsp;&lt;asciidoctor.input.directory&gt;${project.basedir}/docs/asciidoc&lt;/asciidoctor.input.directory&gt;
&nbsp; &nbsp;&lt;generated.asciidoc.directory&gt;${project.build.directory}/asciidoc&lt;/generated.asciidoc.directory&gt;
&nbsp; &nbsp;&lt;asciidoctor.html.output.directory&gt;${project.build.directory}/asciidoc/html&lt;/asciidoctor.html.output.directory&gt;
&nbsp; &nbsp;&lt;asciidoctor.pdf.output.directory&gt;${project.build.directory}/asciidoc/pdf&lt;/asciidoctor.pdf.output.directory&gt;
&lt;/properties&gt;</code></pre>
<p>dependencies依赖配置:</p>
<pre><code>&lt;dependencies&gt;

&nbsp; &nbsp;&lt;!--fastjson依赖--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
        &lt;artifactId&gt;fastjson&lt;/artifactId&gt;
        &lt;version&gt;1.2.46&lt;/version&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--spring-boot依赖--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter&lt;/artifactId&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--测试依赖--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
        &lt;scope&gt;test&lt;/scope&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--web支持--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--swagger2依赖--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
        &lt;version&gt;2.6.1&lt;/version&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--swagger2-ui依赖--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
        &lt;version&gt;2.6.1&lt;/version&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--离线文档--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.restdocs&lt;/groupId&gt;
        &lt;artifactId&gt;spring-restdocs-mockmvc&lt;/artifactId&gt;
        &lt;version&gt;1.1.2.RELEASE&lt;/version&gt;
        &lt;scope&gt;test&lt;/scope&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--生成静态文档--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-staticdocs&lt;/artifactId&gt;
        &lt;version&gt;2.6.1&lt;/version&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&nbsp; &nbsp;&lt;!--lombok依赖--&gt;
&nbsp; &nbsp;&lt;dependency&gt;
        &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
        &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;version&gt;1.16.20&lt;/version&gt;
&nbsp; &nbsp;&lt;/dependency&gt;

&lt;/dependencies&gt;</code></pre>
<p>plugins插件配置:</p>
<pre><code>&lt;build&gt;
&nbsp; &nbsp;&lt;plugins&gt;
        &lt;!--&lt;plugin&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
&nbsp; &nbsp; &nbsp; &nbsp;&lt;/plugin&gt;--&gt;
        &lt;!--Maven通过Maven Surefire Plugin插件执行单元测试--&gt;
        &lt;plugin&gt;
       &nbsp; &nbsp;&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
       &nbsp; &nbsp;&lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
        &lt;!--通过Asciidoctor使得asciidoc生成其他的文档格式,例如:PDF 或者HTML5--&gt;
        &lt;plugin&gt;
       &nbsp; &nbsp;&lt;groupId&gt;org.asciidoctor&lt;/groupId&gt;
       &nbsp; &nbsp;&lt;artifactId&gt;asciidoctor-maven-plugin&lt;/artifactId&gt;
       &nbsp; &nbsp;&lt;version&gt;1.5.3&lt;/version&gt;
       &nbsp; &nbsp;&lt;!--生成PDF--&gt;
       &nbsp; &nbsp;&lt;dependencies&gt;
                &lt;dependency&gt;
               &nbsp; &nbsp;&lt;groupId&gt;org.asciidoctor&lt;/groupId&gt;
               &nbsp; &nbsp;&lt;artifactId&gt;asciidoctorj-pdf&lt;/artifactId&gt;
               &nbsp; &nbsp;&lt;version&gt;1.5.0-alpha.14&lt;/version&gt;
                &lt;/dependency&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;!-- Comment this section to use the default jruby artifact provided by the plugin --&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;dependency&gt;
       &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;groupId&gt;org.jruby&lt;/groupId&gt;
               &nbsp; &nbsp;&lt;artifactId&gt;jruby-complete&lt;/artifactId&gt;
               &nbsp; &nbsp;&lt;version&gt;1.7.21&lt;/version&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;/dependency&gt;
       &nbsp; &nbsp;&lt;/dependencies&gt;

       &nbsp; &nbsp;&lt;!--文档生成配置--&gt;
       &nbsp; &nbsp;&lt;configuration&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;sourceDirectory&gt;${asciidoctor.input.directory}&lt;/sourceDirectory&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;sourceDocumentName&gt;index.adoc&lt;/sourceDocumentName&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;attributes&gt;
               &nbsp; &nbsp;&lt;doctype&gt;book&lt;/doctype&gt;
               &nbsp; &nbsp;&lt;toc&gt;left&lt;/toc&gt;
               &nbsp; &nbsp;&lt;toclevels&gt;3&lt;/toclevels&gt;
               &nbsp; &nbsp;&lt;numbered&gt;&lt;/numbered&gt;
               &nbsp; &nbsp;&lt;hardbreaks&gt;&lt;/hardbreaks&gt;
               &nbsp; &nbsp;&lt;sectlinks&gt;&lt;/sectlinks&gt;
               &nbsp; &nbsp;&lt;sectanchors&gt;&lt;/sectanchors&gt;
               &nbsp; &nbsp;&lt;generated&gt;${generated.asciidoc.directory}&lt;/generated&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;/attributes&gt;
       &nbsp; &nbsp;&lt;/configuration&gt;
       &nbsp; &nbsp;&lt;!--因为每次执行只能处理一个后端,所以对于每个想要的输出类型,都是独立分开执行--&gt;
       &nbsp; &nbsp;&lt;executions&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;!--html5--&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;execution&gt;
               &nbsp; &nbsp;&lt;id&gt;output-html&lt;/id&gt;
               &nbsp; &nbsp;&lt;phase&gt;test&lt;/phase&gt;
               &nbsp; &nbsp;&lt;goals&gt;
               &nbsp; &nbsp; &nbsp; &nbsp;&lt;goal&gt;process-asciidoc&lt;/goal&gt;
               &nbsp; &nbsp;&lt;/goals&gt;
               &nbsp; &nbsp;&lt;configuration&gt;
               &nbsp; &nbsp; &nbsp; &nbsp;&lt;backend&gt;html5&lt;/backend&gt;
               &nbsp; &nbsp; &nbsp; &nbsp;&lt;outputDirectory&gt;${asciidoctor.html.output.directory}&lt;/outputDirectory&gt;
               &nbsp; &nbsp;&lt;/configuration&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;/execution&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;!--pdf--&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;execution&gt;
               &nbsp; &nbsp;&lt;id&gt;output-pdf&lt;/id&gt;
               &nbsp; &nbsp;&lt;phase&gt;test&lt;/phase&gt;
               &nbsp; &nbsp;&lt;goals&gt;
               &nbsp; &nbsp; &nbsp; &nbsp;&lt;goal&gt;process-asciidoc&lt;/goal&gt;
               &nbsp; &nbsp;&lt;/goals&gt;
               &nbsp; &nbsp;&lt;configuration&gt;
               &nbsp; &nbsp; &nbsp; &nbsp;&lt;backend&gt;pdf&lt;/backend&gt;
               &nbsp; &nbsp; &nbsp; &nbsp;&lt;outputDirectory&gt;${asciidoctor.pdf.output.directory}&lt;/outputDirectory&gt;
               &nbsp; &nbsp;&lt;/configuration&gt;
       &nbsp; &nbsp; &nbsp; &nbsp;&lt;/execution&gt;
       &nbsp; &nbsp;&lt;/executions&gt;
&nbsp; &nbsp; &nbsp; &nbsp;&lt;/plugin&gt;
&nbsp; &nbsp;&lt;/plugins&gt;
&lt;/build&gt;</code></pre>
<h2>Swagger2Config配置类</h2>
<pre><code>public class Swagger2Config {
&nbsp; &nbsp;@Bean
&nbsp; &nbsp;public Docket createRestApi() {
&nbsp; &nbsp; &nbsp; &nbsp;return new Docket(DocumentationType.SWAGGER_2)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.apiInfo(apiInfo())
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.select()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.apis(RequestHandlerSelectors.basePackage("com.czq.offline.controller"))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.paths(PathSelectors.any())
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.build();
&nbsp; &nbsp;}

&nbsp; &nbsp;private ApiInfo apiInfo() {
&nbsp; &nbsp; &nbsp; &nbsp;return new ApiInfoBuilder()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.title("springboot利用swagger构建api文档,可以导出离线的HTML和PDF文档")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.description("简单优雅的restfun风格")
// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.termsOfServiceUrl("https://blog.csdn.net/moyanxiaoq")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.contact("chenzhiq")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.version("1.0")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.build();
&nbsp; &nbsp;}
}</code></pre>
<h2>User实体类</h2>
<pre><code>@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description="用户的实体对象")
public class User {

&nbsp; &nbsp;/**
&nbsp; &nbsp; *id
&nbsp; &nbsp; */
&nbsp; &nbsp;@ApiModelProperty(value="用户id",name="id",required=true)
&nbsp; &nbsp;private String id;

&nbsp; &nbsp;/**
&nbsp; &nbsp; *名字
&nbsp; &nbsp; */
&nbsp; &nbsp;@ApiModelProperty(value="用户名",name="name",required=true)
&nbsp; &nbsp;private String name;

&nbsp; &nbsp;/**
&nbsp; &nbsp; *年龄
&nbsp; &nbsp; */
&nbsp; &nbsp;private Integer age;

}</code></pre>
<h2>UserController</h2>
<pre><code>@RestController
@RequestMapping(value = "/user", produces = &nbsp;MediaType.APPLICATION_JSON_VALUE)
@Api(value = "用户信息查询", description = "用户基本信息操作API", tags = "UserController", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

&nbsp; &nbsp;@ApiOperation(value = "/getUser", notes = "根据姓名查询用户信息")
&nbsp; &nbsp;@RequestMapping(value = "getUser", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
&nbsp; &nbsp;@ResponseBody
&nbsp; &nbsp;public User getUser(@RequestParam("name") String name){
&nbsp; &nbsp; &nbsp; &nbsp;User user = new User();
&nbsp; &nbsp; &nbsp; &nbsp;user.setId("123");
&nbsp; &nbsp; &nbsp; &nbsp;user.setName(name);
&nbsp; &nbsp; &nbsp; &nbsp;user.setAge(25);
&nbsp; &nbsp; &nbsp; &nbsp;return user;
&nbsp; &nbsp;}<br>
&nbsp; &nbsp;@ApiOperation(value = "/addUser", notes = "添加一个用户")
&nbsp; &nbsp;@RequestMapping(value = "addUser", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
&nbsp; &nbsp;@ResponseBody
&nbsp; &nbsp;public User addUser(@RequestBody User user){
&nbsp; &nbsp; &nbsp; &nbsp;return user;
&nbsp; &nbsp;}

}</code></pre>
<h2>关键的Test类</h2>
<pre><code>@AutoConfigureMockMvc
@AutoConfigureRestDocs(outputDir = "target/generated-snippets")
@RunWith(SpringRunner.class)
@SpringBootTest
public class Swagger2OfflineApplicationTests {

&nbsp; &nbsp;private String snippetDir = "target/generated-snippets";
&nbsp; &nbsp;private String outputDir &nbsp;= "target/asciidoc";<br>
&nbsp; &nbsp;@Autowired
&nbsp; &nbsp;private MockMvc mockMvc;<br>
&nbsp; &nbsp;@After
&nbsp; &nbsp;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必须和插件里面&lt;generated&gt;&lt;/generated&gt;标签配置一致
        Swagger2MarkupConverter.from(outputDir + "/swagger.json")
                .withPathsGroupedBy(GroupBy.TAGS)// 按tag排序
                .withMarkupLanguage(MarkupLanguage.ASCIIDOC)// 格式
                .withExamples(snippetDir)
                .build()
                .intoFolder(outputDir);// 输出
&nbsp; &nbsp;}

&nbsp; &nbsp;@Test
&nbsp; &nbsp;public void Test2() throws Exception{
        User user = new User();
        user.setId("123");
        user.setName("FLY");
        user.setAge(25);
        mockMvc.perform(post("/user/addUser").contentType(MediaType.APPLICATION_JSON)
                .content(JSON.toJSONString(user))
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().is2xxSuccessful())
                .andDo(MockMvcRestDocumentation.document("addUser", preprocessResponse(prettyPrint())));
&nbsp; &nbsp;}

&nbsp; &nbsp;@Test
&nbsp; &nbsp;public void TestApi() throws Exception {
&nbsp; &nbsp; &nbsp; &nbsp;mockMvc.perform(get("/user/getUser").param("name", "FLY")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andDo(MockMvcRestDocumentation.document("getUser", preprocessResponse(prettyPrint())));

&nbsp; &nbsp;}

}</code></pre>
<h2>目录结构以及yml配置</h2>
<p><img src="https://img2018.cnblogs.com/common/1505525/202001/1505525-20200121142324069-1083383511.png"></p>
<h2>测试</h2>
<p>代码完成后就可启动项目访问:http://localhost:9999/swagger-ui.html&nbsp;这个是在线的文档显示。导出离线的需要进入当前项目的根路径执行命令:<code>mvn clean test</code></p>
<p><img src="https://img2018.cnblogs.com/common/1505525/202001/1505525-20200121142335593-1091677741.png"></p>
<p>如果接口多的话命令会执行一会。结束后在&nbsp;<code>target/asciidoc</code>&nbsp;目录下会生成两个文件夹分别是&nbsp;<code>html</code>&nbsp;和&nbsp;<code>pdf</code>&nbsp;里面就是对应格式的离线文档了。</p>
<p><img src="https://img2018.cnblogs.com/common/1505525/202001/1505525-20200121142351728-1461546836.png"></p>
<p>如果你的测试用例都写了而且可以测试通过,这个例子也将会显示在导出的离线文档里。</p>
<p><img src="https://img2018.cnblogs.com/common/1505525/202001/1505525-20200121142408799-1262594854.png"></p>
<p><img src="https://img2018.cnblogs.com/common/1505525/202001/1505525-20200121142423663-1980479807.png"></p>
<p>&nbsp;</p>
<p><strong>获取源码地址:</strong>https://github.com/moyanxiaoq/boot-demo/tree/master/swagger-offline</p><br><br>
来源:https://www.cnblogs.com/breka/p/12221614.html
頁: [1]
查看完整版本: 项目工具三:Swagger导出离线版HTML5和PDF格式api文档