【原】swagger2markup+swagger2+springboot+spring mvc rest +adoc 在线文档生成

首先我们看一看效果图,高大上有木有啊有木有。

image.png

image.png

主要参考
https://leongfeng.github.io/2017/02/20/springboot-springfox-swagger2markup-spring-restdoc/
https://github.com/Swagger2Markup/spring-swagger2markup-demo
http://swagger2markup.github.io/swagger2markup/1.3.1/

这里笔者主要是参照官网的spring-swagger2markup-demo进行参考搭建的。

接下来我们来说一下是如何搞定的

1.在项目增加一个文件夹docs

直接从spring-swagger2markup-demo里把这个文件夹复制到自己的项目里就可以,复制的时候还有个manual_content1.adoc 和 manual_content2.adoc的文件,这个文件是接口文档的自定义说明部分,我用不到所以就删了,文件删除之后还要把index.adoc里对应的地方也删掉。


image.png

index.adoc 内容如下:

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

2.项目中添加maven依赖和插件

properties部分


        UTF-8
        UTF-8
        1.8
        1.5.8.RELEASE
        
        19.0
        2.5.0
        
        4.2.1
        2.0
        3.10-beta2
        0.1.46
        3.3.2
        1.8.3
        1.4.1
        1.2.0

dependency部分


        
            io.swagger
            swagger-annotations
            1.5.6
        
        
            com.google.guava
            guava
            ${com.google.guava.version}
        
        
            com.fasterxml.jackson.dataformat
            jackson-dataformat-smile
        
        
            com.fasterxml.jackson.module
            jackson-module-afterburner
        
        
            io.springfox
            springfox-swagger2
            ${springfox.version}
            test
        
        
            io.springfox
            springfox-swagger-ui
            ${springfox.version}
        
        
            io.springfox
            springfox-bean-validators
            ${springfox.version}
           
        
        
            io.springfox
            springfox-staticdocs
            ${springfox.version}
        
        
            org.springframework.restdocs
            spring-restdocs-mockmvc
        
        
            com.fasterxml.jackson.module
            jackson-module-jsonSchema
            test
        
        
            io.github.robwin
            assertj-swagger
            0.2.0
            test
        
        
            io.github.swagger2markup
            swagger2markup-spring-restdocs-ext
            ${swagger2markup.version}
            test
        

        
        
            org.slf4j
            slf4j-api
            
        
            org.slf4j
            jcl-over-slf4j
            runtime
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

maven plugin部分


    
        

            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
            
                com.spotify
                docker-maven-plugin
                0.4.13
                
                    true
                    
                    10.0.0.95:5000/JAVA_Account-Service:${project.version}
                    ${project.basedir}/src/main/docker
                    
                        
                            /
                            ${project.build.directory}
                            ${project.build.finalName}.jar
                        
                    
                
            

            
            
                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.3
                
                
                    
                        org.asciidoctor
                        asciidoctorj-pdf
                        1.5.0-alpha.10.1
                    
                    
                        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
                2.4
                
                    
                        
                            true
                            lib/
                            io.github.robwin.swagger2markup.petstore.Application
                        
                    
                
            

            
            
                maven-dependency-plugin
                
                    
                        package
                        
                            copy-dependencies
                        
                        
                            ${project.build.directory}/lib
                        
                    
                
            

            
            
                maven-resources-plugin
                2.7
                
                    
                        copy-resources
                        prepare-package
                        
                            copy-resources
                        
                        
                            ${project.build.outputDirectory}/static/docs
                            
                                
                                    ${asciidoctor.html.output.directory}
                                
                                
                                    ${asciidoctor.pdf.output.directory}
                                
                            
                        
                    
                
            
            
        
    

这里需要做如下几点说明

  1. log4j2的部分,官方的demo中使用的logback,但是我的项目里用的是log4j2,所以这里我有做处理,slf4j换成了log4j2,撰文时,官方的demo里用springfox.version是2.4.0,笔者全部升级为了2.5.0

3.swagger的使用部分

SwaggerConfig代码

package com.weds.account;

import com.google.common.base.Predicates;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
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;

import com.google.common.base.Predicates;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Arrays;

import static java.util.Arrays.asList;
import static springfox.documentation.builders.PathSelectors.ant;

@EnableSwagger2
@Configuration
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {

    @Bean
    public Docket restApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.xx.account.web")).paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("账户微服务接口文档")
                .description("账户微服务接口文档")
                .contact(new Contact("XXX数据", "http://www.weds.com.cn", "[email protected]"))
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("1.0.0")
                .build();
    }
}

注意:如果你是参照官方文档添加的maven依赖,那这个SwaggerConfig 必须是在单元测试的包模块下,因为官方的依赖里springfox-bean-validators的scope是test,只有在test下才会被成功引入到SwaggerConfig 中。restApi部分采用的是basePackage方式,这样可以忽略比如actuator的rest接口也被拿来生成了接口文档。

3.REST接口以及实体类定义部分

package com.weds.account.web.inter;

import com.weds.framework.core.common.model.PagedResponse;

import com.weds.account.entity.req.*;
import com.weds.account.entity.resp.App_Account_Query_Resp;
import com.weds.framework.core.common.model.JsonResult;

import org.springframework.web.bind.annotation.*;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

/**
 * 应用账户相关的接口
 * Created by Caoheyang on 2018-01-06.
 */
@RestController
@RequestMapping(value = "/app/account", produces = {APPLICATION_JSON_VALUE})
public interface AppAccountInter {
    /**
     * 新增应用账户接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @param req
     * @return
     */
    @PutMapping(path = "/add")
    public JsonResult add(@RequestBody App_Account_Add_Req req);

    /**
     * 修改应用账户信息接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @param req
     * @return
     */
    @PostMapping(path = "/modify")
    public JsonResult modify(@RequestBody App_Account_Modify_Req req);

    /**
     * 修改应用账户密码接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @param req
     * @return
     */
    @PostMapping(path = "/modify_pass")
    public JsonResult modifyPass(@RequestBody App_Account_Modify_Pass_Req req);

    /**
     * 删除应用账户接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @param app_account 应用账户
     * @return
     */
    @DeleteMapping(path = "/delete/{app_account}")
    public JsonResult delete(@PathVariable("app_account") String app_account);

    /**
     * 查询某个应用账户接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @return
     */
    @PostMapping(path = "/searchone")
    public JsonResult searchOne();

    /**
     * 查询应用账户列表接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @return
     */
    @PostMapping(path = "/search")
    public JsonResult> search();

    /**
     * 应用账户登陆接口
     * Created by Caoheyang on 2018-01-06.
     *
     * @param req
     * @return
     */
    @PostMapping(path = "/login")
    public JsonResult login(@RequestBody App_Account_Login_Req req);
}

package com.weds.account.web;

import com.weds.account.entity.req.*;
import com.weds.account.entity.resp.App_Account_Query_Resp;
import com.weds.account.web.inter.AppAccountInter;
import com.weds.framework.core.common.model.JsonResult;
import com.weds.framework.core.common.model.PagedResponse;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;

/**
 * 应用账户相关服务的Controller Created by Caoheyang on 2018-01-06.
 */
@RestController
@Api(value = "应用账户管理", description = "应用账户相关的接口")
public class AppAccountController implements AppAccountInter {

    @ApiOperation(value = "新增应用账户接口", notes = "新增应用账户接口")
    @Override
    public JsonResult add(
            @ApiParam(value = "入参", required = true) @RequestBody App_Account_Add_Req req) {
        return null;
    }

    @ApiOperation(value = "修改应用账户信息接口", notes = "修改应用账户信息接口")
    @Override
    public JsonResult modify(
            @ApiParam(value = "入参", required = true) @RequestBody App_Account_Modify_Req req) {
        return null;
    }

    @ApiOperation(value = "修改应用账户密码接口", notes = "修改应用账户密码接口")
    @Override
    public JsonResult modifyPass(
            @ApiParam(value = "入参", required = true) @RequestBody App_Account_Modify_Pass_Req req) {
        return null;
    }

    @ApiOperation(value = "删除应用账户接口", notes = "删除应用账户接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "app_account", value = "应用账户", required = true, paramType = "path", dataType = "string")})
    @Override
    public JsonResult delete(@PathVariable("app_account") String app_account) {
        return null;
    }

    @ApiOperation(value = "查询某个应用账户接口", notes = "查询某个应用账户接口")
    @Override
    public  JsonResult  searchOne() {
        return null;
    }

    @ApiOperation(value = "查询应用账户列表接口", notes = "查询应用账户列表接口")
    @Override
    public JsonResult>  search() {
        return null;
    }

    @ApiOperation(value = "应用账户登陆接口", notes = "应用账户登陆接口")
    @Override
    public  JsonResult  login(
            @ApiParam(value = "入参", required = true) @RequestBody App_Account_Login_Req req) {
        return null;
    }
}
package com.weds.account.entity.req;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.Map;

/**
 * 新增应用账户接口 入参
 * Created by Caoheyang on 2018-01-08.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class App_Account_Add_Req implements Serializable{

    //应用账户,用一个应用下的应用账户具有唯一性
    @ApiModelProperty(required = true, dataType = "string", example = "admin",
            value = "应用账户,用一个应用下的应用账户具有唯一性")
    @Getter
    @Setter
    private String app_account;

    //应用账户标签,json格式,根据平台入驻时与账户微服务预定义的标签,由平台应用自行存储
    @ApiModelProperty(required = true, dataType = "string", example = "{\"class\":\"03\",\"type\":\"teacher\"}",
            value = "应用账户标签,json格式,根据平台入驻时与账户微服务预定义的标签,由平台应用自行存储")
    @Getter
    @Setter
    private Map label;

    //应用账户密码 DES加密之后的密文
    @ApiModelProperty(required = true, dataType = "string", example = "FFWERWRFGP34DFWE",
            value = "应用账户密码,DES加密之后的密文")
    @Getter
    @Setter
    private String password;

    //角色编码,以”;角色码1;角色码2;”形式存储
    @ApiModelProperty(required = false, dataType = "string", example = ";0011;0032;",
            value = "角色编码,以”;角色码1;角色码2;”形式存储")
    @Getter
    @Setter
    private String role_nos;

    //添加人 默认值为system
    @ApiModelProperty(required = false, dataType = "string", example = "system",
            value = "添加人 默认值为system")
    @Getter
    @Setter
    private String inserted_by;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注1",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_1;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注2",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_2;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注3",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_3;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注4",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_4;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注5",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_5;
}

package com.weds.account.entity.resp;

import com.weds.account.entity.App_Account_Role;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import javax.management.relation.Role;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * 查询某个应用账户接口 返回数据结构
 * Created by Caoheyang on 2018-01-09.
 */
public class App_Account_Query_Resp implements Serializable {
    //基础账户编号
    @ApiModelProperty(required = true, dataType = "int", example = "123",
            value = "基础账户编号")
    @Getter
    @Setter
    private int basic_acc_no;

    //应用账户
    @ApiModelProperty(required = true, dataType = "string", example = "admin",
            value = "应用账户")
    @Getter
    @Setter
    private String app_account;

    //身份证号码
    @ApiModelProperty(required = false, dataType = "string", example = "370685199010111215",
            value = "身份证号码")
    @Getter
    @Setter
    private String id_card;

    //真实姓名
    @ApiModelProperty(required = true, dataType = "string", example = "张三",
            value = "真实姓名")
    @Getter
    @Setter
    private String name;

    //性别 男 女
    @ApiModelProperty(required = true, dataType = "int", example = "男",
            value = "性别")
    @Getter
    @Setter
    private String sex;

    //手机号码1
    @ApiModelProperty(required = false, dataType = "string", example = "18888888888",
            value = "手机号码1")
    @Getter
    @Setter
    private String mobilephone;

    //手机号码2
    @ApiModelProperty(required = false, dataType = "string", example = "18888888888",
            value = "手机号码2")
    @Getter
    @Setter
    private String mobilephone2;

    //固话
    @ApiModelProperty(required = false, dataType = "string", example = "0535-80888888",
            value = "固话")
    @Getter
    @Setter
    private String telephone;

    //邮箱1
    @ApiModelProperty(required = false, dataType = "string", example = "[email protected]",
            value = "邮箱1")
    @Getter
    @Setter
    private String email;

    //邮箱2
    @ApiModelProperty(required = false, dataType = "string", example = "[email protected]",
            value = "邮箱2")
    @Getter
    @Setter
    private String email2;

    //应用账户状态
    @ApiModelProperty(required = true, dataType = "int", example = "0",
            value = "应用账户状态")
    @Getter
    @Setter
    private int app_account_status;

    //标签  JSON数据体
    @ApiModelProperty(required = true, dataType = "string", example = "{\"class\":\"03\",\"type\":\"teacher\"}",
            value = "应用账户标签,json格式,根据平台入驻时与账户微服务预定义的标签,由平台应用自行存储")
    @Getter
    @Setter
    private Map label;

    //角色,信息 JONS 数组
    @ApiModelProperty(required = true, dataType = "string", example = "admin",
            value = "角色数组")
    @Getter
    @Setter
    private List role_nos;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注1",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_1;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注2",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_2;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注3",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_3;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注4",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_4;

    //备用标注,应用自定义存储,自行解析
    @ApiModelProperty(required = false, dataType = "string", example = "备注5",
            value = "备用标注,应用自定义存储,自行解析")
    @Getter
    @Setter
    private String column_5;
}

注意:
1.我这里用了lombok所以没有get,set方法,只有 @Getter和@Setter注解
2.在与swagger2markup的集成中发现,必须用@ApiParam注解,而且实体上不可以加ApiModel否则swagger2markup部分的生成会失败,无法映射接口的入参和返回值部分。
3.在swagger2中response部分不需要写,关于泛型部分也会自己直接映射的。

  1. @ApiImplicitParam在 @RequestBody联合使用的过程中,在swaggerui下没问题,但是在swagger2markup下是有问题的,无法生成swagger2markup文档,需要注意!

4.使用swagger2markup生成文档

1.需要新建一个Swagger2MarkupTest类,代码如下

package com.weds.account;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.SpringJUnit4ClassRunner;
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.restdocs.mockmvc.MockMvcRestDocumentation.document;
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.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@AutoConfigureRestDocs(outputDir = "build/asciidoc/snippets")
@SpringBootTest(classes = {AccountServiceApplication.class, SwaggerConfig.class})
@AutoConfigureMockMvc
public class Swagger2MarkupTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void createSpringfoxSwaggerJson() throws Exception {
        //String designFirstSwaggerLocation = Swagger2MarkupTest.class.getResource("/swagger.yaml").getPath();

        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);
        }
    }
}

2.执行maven 的clean命令 然后执行test命令

image.png

插件会自动生成接口文件到target下,文件是adoc格式的。
由于插件会提现把接口文档生成为adoc文件,放到target下,所以对项目的实际运行速度毫无影响。
这时如果执行spring boot的run 命令,请求http://localhost:1234/docs/index.html发现是500的错误,但是如果把执行maven的package命令,然后在执行spring boot的run 命令或者 java -jar 项目.jar 把项目跑起来,然后在访问 http://localhost:1234/docs/index.html 就ok了,并且 http://localhost:1234/swagger-ui.html 也是ok的。

github开源代码:https://github.com/caoheyang/swagger2markupdemo
oschina开源代码:https://gitee.com/caoheyang/swagger2markupdemo
请注意代码中还有很多的依赖是自己封装的,请参照博客查找swagger2markup部分即可。

你可能感兴趣的:(【原】swagger2markup+swagger2+springboot+spring mvc rest +adoc 在线文档生成)