1

上一篇中,我们介绍了基本的API文档的生成方法,并最后成功的发布为html文件。

本节接上篇内容,进行文件的自动拼接,并且在出具API文件时,自动加入请求参数、主体的相关信息。

本文使用工具:

springFox

传送门:SpringFox官方文档

加入依赖

删除依赖

我们此时,修改测试代码片段的接拼方式,所以删除原插件的配置信息。

        <plugin> 
            <groupId>org.asciidoctor</groupId>
            <artifactId>asciidoctor-maven-plugin</artifactId>
            <version>1.5.3</version>
            <executions>
                <execution>
                    <id>generate-docs</id>
                    <phase>prepare-package</phase> 
                    <goals>
                        <goal>process-asciidoc</goal>
                    </goals>
                    <configuration>
                        <backend>html</backend>
                        <doctype>book</doctype>
                    </configuration>
                </execution>
            </executions>
            <dependencies>
                <dependency> 
                    <groupId>org.springframework.restdocs</groupId>
                    <artifactId>spring-restdocs-asciidoctor</artifactId>
                    <version>2.0.1.RELEASE</version>
                </dependency>
            </dependencies>
        </plugin>

增加依赖

<repositories>
    <repository>
      <id>jcenter-snapshots</id>
      <name>jcenter</name>
      <url>http://oss.jfrog.org/artifactory/oss-snapshot-local/</url>
    </repository>
</repositories>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-data-rest</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>2.9.0</version>
        </dependency>

注意 加入到 pom.xml中的相应标签中。其中repositories为一级标签。即repositories标签的父级为<project>

配置项目

新建SwaggerConfig.(注意,该文件应该位于项目启动文件的同级或下级文件夹)

package com.mengyunzhi.check_apply_online.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableSwagger2 // 启用Swagger2
@Configuration  // 表明这是个配置文件,Spring在启动时,需要对本文件进行扫描
public class SwaggerConfig implements WebMvcConfigurer {

}

测试

启动项目,访问http://localhost:8080/swagger-ui.html, 将显示以下界面。

clipboard.png

加入Swagger的相关注解后,会对应的生成中文说明。比如:

package com.mengyunzhi.check_apply_online.controller;

import com.mengyunzhi.check_apply_online.entity.DanWei;
import com.mengyunzhi.check_apply_online.service.DanWeiService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.Principal;

@RestController
@RequestMapping("/DanWei")
@Api(value = "/DanWei", description = "单位")
public class DanWeiController {
    private final static Logger logger = LoggerFactory.getLogger(DanWeiController.class);

    @Autowired
    DanWeiService danWeiService;

    @GetMapping("/login")
    @ApiOperation(value = "登录", nickname = "其实登录不限地址,任意地址均可以登录。")
    public DanWei login(Principal user) {
        DanWei currentLoginDanWei = danWeiService.getCurrentLoginDanWei();
        return currentLoginDanWei;
    }

    @GetMapping("/logout")
    @ApiOperation(value = "logout 注销", nickname = "DanWei_logout",
            notes = "参考资料:http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html")
    public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        logger.info("-----------------注销----------------");
        danWeiService.setCurrentLoginDanWei(null);
        // 获取当前认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        // 如果存在认证信息则调用注销操作
        if (null != authentication) {
            new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);
        }

        return;
    }

    @PostMapping("/register")
    @ApiOperation(value = "注册新单位")
    public void save(@RequestBody DanWei danWei) {
        danWeiService.save(danWei);
    }
}

则生成的中文说明如下:

clipboard.png

自定义文档

Swigger支持自定义文件,即可以对html的标题等信息进行定制。下面给出示例:

package com.mengyunzhi.check_apply_online.config;

import com.google.common.base.Predicates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
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;

@EnableSwagger2 // 启用Swagger2
@Configuration  // 表明这是个配置文件,Spring在启动时,需要对本文件进行扫描
@ComponentScan(basePackageClasses = com.mengyunzhi.check_apply_online.controller.DanWeiController.class)
public class SwaggerConfig implements WebMvcConfigurer {
    private static final Logger logger = LoggerFactory.getLogger(SwaggerConfig.class);

    /**
     * 创建Docket,写用配置信息
     * @return
     */
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(this.apiInfo())
                .select()
                // 加入除'error'外的所有请求信息
                .paths(Predicates.and(PathSelectors.ant("/**"), Predicates.not(PathSelectors.ant("/error"))))
                .build()
                // 忽略的参数类型(ApiIgnore注解的字段)
                .ignoredParameterTypes(ApiIgnore.class)
                .enableUrlTemplating(true);
    }

    /**
     * 返回一个带有项目设置信息的ApiInfoBuilder
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("网上送检系统API")
                .description("为检定软件提供网上送检的支持")
                .contact(new Contact("panjie", "http://www.mengyunzhi.com", "3792535@qq.com"))
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("1.0.0")
                .build();
    }
}

则再次启动时,将使用我们自定义的配置设置标题等信息。

Swigger与Spring REST Docs配置

swigger的优点是可以获取到所有的控制器及方法,并且能够按列表的形式展现出来。 Spring REST Docs的优点是只可以将测试数据直接展现给前台的用户。本节将阐述如何将二者结合起来使用。

加入依赖

    <!--jcenter仓库插件地址 (必须有,不然有些包下载不到。。。)-->
    <pluginRepositories>
        <pluginRepository>
            <id>jcenter</id>
            <name>jcenter maven</name>
            <url>http://jcenter.bintray.com</url>
        </pluginRepository>
    </pluginRepositories>
    <properties>
        <!-- 其它配置信息-->

        <!--swgger2markup 配置信息-->
        <swagger2markup.version>1.2.0</swagger2markup.version>
        <asciidoctor.input.directory>${project.basedir}/src/main/asciidoc</asciidoctor.input.directory>
        <swagger.output.dir>${project.build.directory}/swagger</swagger.output.dir>
        <swagger.snippetOutput.dir>${project.build.directory}/generated-snippets</swagger.snippetOutput.dir>
        <generated.asciidoc.directory>${project.build.directory}/asciidoc/generated</generated.asciidoc.directory>
        <asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
        <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
        <swagger.input>${swagger.output.dir}/swagger.json</swagger.input>
    </properties>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-staticdocs</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.github.robwin</groupId>
            <artifactId>assertj-swagger</artifactId>
            <version>0.2.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.github.swagger2markup</groupId>
            <artifactId>swagger2markup-spring-restdocs-ext</artifactId>
            <version>${swagger2markup.version}</version>
            <scope>test</scope>
        </dependency>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <systemPropertyVariables>
                        <!--设置变量信息-->
                        <io.springfox.staticdocs.outputDir>${swagger.output.dir}</io.springfox.staticdocs.outputDir>
                        <io.springfox.staticdocs.snippetsOutputDir>${swagger.snippetOutput.dir}</io.springfox.staticdocs.snippetsOutputDir>
                    </systemPropertyVariables>
                </configuration>
            </plugin>

            <!-- 首先,生成asciidoc(.adoc) -->
            <plugin>
                <groupId>io.github.swagger2markup</groupId>
                <artifactId>swagger2markup-maven-plugin</artifactId>
                <version>${swagger2markup.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>io.github.swagger2markup</groupId>
                        <artifactId>swagger2markup-import-files-ext</artifactId>
                        <version>${swagger2markup.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>io.github.swagger2markup</groupId>
                        <artifactId>swagger2markup-spring-restdocs-ext</artifactId>
                        <version>${swagger2markup.version}</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <swaggerInput>${swagger.input}</swaggerInput>
                    <outputDir>${generated.asciidoc.directory}</outputDir>
                    <config>
                        <swagger2markup.markupLanguage>ASCIIDOC</swagger2markup.markupLanguage>
                        <swagger2markup.pathsGroupedBy>TAGS</swagger2markup.pathsGroupedBy>

                        <swagger2markup.extensions.dynamicOverview.contentPath>${project.basedir}/src/main/asciidoc/extensions/overview</swagger2markup.extensions.dynamicOverview.contentPath>
                        <swagger2markup.extensions.dynamicDefinitions.contentPath>${project.basedir}/src/main/asciidoc/extensions/definitions</swagger2markup.extensions.dynamicDefinitions.contentPath>
                        <swagger2markup.extensions.dynamicPaths.contentPath>${project.basedir}/src/main/asciidoc/extensions/paths</swagger2markup.extensions.dynamicPaths.contentPath>
                        <swagger2markup.extensions.dynamicSecurity.contentPath>${project.basedir}src/main/asciidoc/extensions/security/</swagger2markup.extensions.dynamicSecurity.contentPath>

                        <swagger2markup.extensions.springRestDocs.snippetBaseUri>${swagger.snippetOutput.dir}</swagger2markup.extensions.springRestDocs.snippetBaseUri>
                        <swagger2markup.extensions.springRestDocs.defaultSnippets>true</swagger2markup.extensions.springRestDocs.defaultSnippets>
                    </config>
                </configuration>
                <executions>
                    <execution>
                        <phase>test</phase>
                        <goals>
                            <goal>convertSwagger2markup</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!-- 然后,使用Asciidoctor(前面在spring rest docs时已安装,在这需要加部分依赖)将.adoc转成pdf和html -->
            <plugin>
                <groupId>org.asciidoctor</groupId>
                <artifactId>asciidoctor-maven-plugin</artifactId>
                <version>1.5.3</version>
                <!-- PDF依赖 -->
                <dependencies>
                    <dependency>
                        <groupId>org.asciidoctor</groupId>
                        <artifactId>asciidoctorj-pdf</artifactId>
                        <version>1.5.0-alpha.10.1</version>
                    </dependency>
                    <dependency>
                        <groupId>org.jruby</groupId>
                        <artifactId>jruby-complete</artifactId>
                        <version>1.7.21</version>
                    </dependency>
                </dependencies>
                <!-- 生成文件时的配置信息 -->
                <configuration>
                    <sourceDirectory>${asciidoctor.input.directory}</sourceDirectory>
                    <sourceDocumentName>index.adoc</sourceDocumentName>
                    <attributes>
                        <doctype>book</doctype>
                        <toc>left</toc>
                        <toclevels>3</toclevels>
                        <numbered></numbered>
                        <hardbreaks></hardbreaks>
                        <sectlinks></sectlinks>
                        <sectanchors></sectanchors>
                        <generated>${generated.asciidoc.directory}</generated>
                    </attributes>
                </configuration>
                <!-- Since each execution can only handle one backend, run
                     separate executions for each desired output type -->
                <executions>
                    <!--生成html-->
                    <execution>
                        <id>output-html</id>
                        <phase>test</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>html5</backend>
                            <outputDirectory>${asciidoctor.html.output.directory}</outputDirectory>
                        </configuration>
                    </execution>

                    <!--生成pdf-->
                    <execution>
                        <id>output-pdf</id>
                        <phase>test</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>pdf</backend>
                            <outputDirectory>${asciidoctor.pdf.output.directory}</outputDirectory>
                        </configuration>
                    </execution>

                </executions>
            </plugin>

测试

执行mvn package -Dmaven.test.skip=true跳过测试,直接进行打包。第一次会下载插件,时间长短看网速。
最终将得到如下错误:

clipboard.png

target下的swagger.json未生成。该文件即上个章节中的查看/v2/api-docs提到的json文件。下面,我们增加一个单元测试文件,运行其的作用是访问/v2/api-docs,抓取返回的内容,并存储为:swagger.json文件。

package com.mengyunzhi.check_apply_online.controller;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;

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;

public class SwaggerTest extends ControllerTest {
    private final static Logger logger = LoggerFactory.getLogger(SwaggerTest.class);
    @Test
    public void createSwaggerJson() throws Exception {
        logger.info("------ 获取用于生成自动文档的swagger.json文件 ------");
        String swaggerJson = this.mockMvc.perform(get("/v2/api-docs").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();

        // 对应pom.xml中 <systemPropertyVariables> 设置的属性值
//        String outputDir = System.getProperty("io.springfox.staticdocs.outputDir");
        String outputDir = "target/swagger";
        Files.createDirectories(Paths.get(outputDir));

        try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputDir, "swagger.json"), StandardCharsets.UTF_8)) {
            writer.write(swaggerJson);
        }

        return;
    }
}

执行单元测试,将得到target/swagger/swagger.json, 同时,我们修改src/main/asciidoc/index.adoc文件,使其包含插件自动生成的四个文件。

include::{generated}/overview.adoc[]
include::{generated}/paths.adoc[]
include::{generated}/security.adoc[]
include::{generated}/definitions.adoc[]

最后,我们再次执行mvn package -Dmaven.test.skip=true

将得到以下文件:

clipboard.png

其中html文件夹,为生成的html格式的API文档。pdf文件夹中为生成的pdf文档。我们进行html文件夹,更可以浏览生成的html文档了。

clipboard.png

关联swagger与Spring rest docs

我们swagger的HTML文件也有了,spring rest docs的测试文件也有了,那么如何进行关联呢?
很简单,我们只需要为控制器的相应方法与测试时生成的片段名称起想同的名字就可以了。

比如:

    @PostMapping("/register")
    @ApiOperation(value = "注册新单位", nickname = "DanWei_register")
    public void save(@RequestBody DanWei danWei) {
        danWeiService.save(danWei);
    }

测试文件:

    @Test
    public void registerTest() throws Exception {
        String url = "/DanWei/register";
        this.mockMvc.perform(MockMvcRequestBuilders
                .post(url)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content("{}"))
                .andExpect(MockMvcResultMatchers.status().isOk())
        .andDo(MockMvcRestDocumentation.document("DanWei_register"));
    }

执行mvn package后,相关的测试片段,则自动加入到了相应的位置上。

其实了除了起一个相同的以外,还取于这个配置:<swagger.snippetOutput.dir>${project.build.directory}/generated-snippets</swagger.snippetOutput.dir>,是它告诉了Swagger:spring rest docs生成的测试代码片段在的位置。

自定义HTML内容

我们可以修改src/main/asciidoc/index.adoc来达到更改生成的html内容的目的。比如,我们加入一些测试的说明。

include::{generated}/overview.adoc[]
== 说明
当前版本:1.0

=== 开发环境
java1.8 + maven3.3

=== 测试用户
用户名: user1, 密码: user1

include::{generated}/paths.adoc[]
include::{generated}/security.adoc[]
include::{generated}/definitions.adoc[]

则重新mvn package后,将生成如下html:

clipboard.png

总结

在本节中,我们主要使用了一个插件进行了文档的自动生成。插件大概替我们做了如下的事情:

  1. 通过swagger.json来生成.adoc
  2. 自动加载测试代码片段
  3. 利用src/main/asciidoc/index.adoc生成htmlpdf.

其实,为了给swagger提供可用的json,我们写了单元测试文件。

在下一节中,我们将阐述如何使用自动测试机器人结合github,实现自动发布API文档。

参考非官方教程:

https://leongfeng.github.io/2...

潘杰
3.1k 声望239 粉丝