IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Spring Boot [如何优雅的编写文档]

    yangrd发表于 2017-05-09 11:46:40
    love 0

    导读:

    在团队协作的时候许多时候需要用到接口文档,我们通常通过手工编写大量重复格式的文档,让我想起了程序员最讨厌的两件事:没有文档,编写文档。哈哈,如果使用过swagger的朋友应该都很了解它带给我们的便利,如果你还没有使用swagger的话,正好打算编写RESTful API文档,这里有一篇文章Spring Boot中使用Swagger2构建强大的RESTful API文档可以帮助你在较短的时间内构建出一个线上文档,有些时候我们需要生成离线文档有该怎么做呢?带着这个问题我们一起去出发。

    关于swagger:

    Swagger - 前后端分离后的契约

    通过Swagger进行API设计,与Tony Tam的一次对话

    一个简短的总结,更好的生成RESTful API文档,提供相应的测试功能,效果图如下:

    swagger

    编写离线文档:

    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。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.restdocs</groupId>
        <artifactId>spring-restdocs-mockmvc</artifactId>
        <version>1.1.2.RELEASE</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-staticdocs</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.8</version>
    </dependency>

    使用Maven插件:

    我们使用asciidoctor-maven-plugin插件将Asciidoc格式转成HTML5格式
    了解更多: 使用介绍

     <plugin>
        <groupId>org.asciidoctor</groupId>
        <artifactId>asciidoctor-maven-plugin</artifactId>
        <configuration>
            <!--配置index.adoc的获取路径-->
            <!--<sourceDirectory>${asciidoctor.input.directory}</sourceDirectory>-->
            <outputDirectory>${asciidoctor.html.output.directory}</outputDirectory>
            <sourceDocumentName>index.adoc</sourceDocumentName>
            <attributes>
                <doctype>book</doctype>
                <toc>left</toc>
                <toclevels>3</toclevels>
                <generated>${generated.asciidoc.directory}</generated>
            </attributes>
        </configuration>
        <executions>
            <execution>
                <id>output-html</id>
                <phase>test</phase>
                <goals>
                    <goal>process-asciidoc</goal>
                </goals>
                <configuration>
                    <backend>html</backend>
                    <attributes>
                        <snippets>${project.build.directory}/generated-snippets</snippets>
                    </attributes>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    编写测试类生成离线文档:

    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必须和插件里面<generated></generated>标签配置一致
            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的插件,只需要执行测试就可以生成相应的文档, 效果图如下:

    Spring REST Docs

    补充:

    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



沪ICP备19023445号-2号
友情链接