基于Spring的Restful接口生成工具

场景

有时候需要为前端开发者提供Restful Api说明文档,通过word文档创建和修改非常耗时,希望有一种比较便捷的第三方库可以减少生成Api说明文档的工作量

基于Spring的Restful Api生成工具

术语解析

  • Springfox-swagger2
    Swagger是一个可以生成基于多种语言编写的Restful Api的文档生成工具,详见这里。
    查看Springfox-swagger2注解文档,请点击这里
  • Swagger2markup
    Swagger2markup是一个使用java编写的将Swagger语义转换成markdown、asciidoc文本格式的开源项目
    Swagger2Markerup的详细说明请见这里
  • Asciidoc
    AsciiDoc是一种MarkDown的扩展文本格式,AsciiDoc相比MarkDown更适合编写类似API文档,学术文档这样的文档。
    详见这里
  • Asciidoctor
    asciidoctor是一个由ruby编写的可以将 asciidoc转换成html、pdf的开源项目,这个项目有java版本和maven插件,详见这里

接口生成原理

  • generate an up-to-date Swagger JSON file during an unit or integration test
    使用Springfox-swagger2生成swagger json文件
  • convert the Swagger JSON file into AsciiDoc
    使用Swagger2markup将swagger json文件转换成asciidoc文档片段
  • add hand-written AsciiDoc documentation
    编写asciidoc的文档(主要是组装步骤2中生成的asciidoc文档片段)
  • convert AsciiDoc into HTML and PDF
    使用Asciidoctor将asciidoc转换成HTML 或pdf

Swagger部署说明

  1. 在pom引入下面依赖:
        
            io.springfox
            springfox-swagger2
            2.4.0
            test
        
  1. 配置一个SwaggerConfig
@EnableSwagger2
@Configuration
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {

    @Bean
    public Docket restApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .securitySchemes(asList(
                        new OAuth(
                            "petstore_auth",
                            asList(new AuthorizationScope("write_pets", "modify pets in your account"),
                                    new AuthorizationScope("read_pets", "read your pets")),
                                Arrays.asList(new ImplicitGrant(new LoginEndpoint("http://petstore.swagger.io/api/oauth/dialog"), "tokenName"))
                        ),
                        new ApiKey("api_key", "api_key", "header")
                ))
                .select()
                .paths(Predicates.and(ant("/**"), Predicates.not(ant("/error")), Predicates.not(ant("/management/**")), Predicates.not(ant("/management*"))))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Swagger Petstore")
                .description("Petstore API Description")
                .contact(new Contact("TestName", "http:/test-url.com", "[email protected]"))
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("1.0.0")
                .build();
    }
}
  1. 运行Spring项目
    运行项目后会发布Spring的路由器多了若干个对外的接口,其中一个是/v2/api-docs/
    通过这个接口可以获取到由Swagger生成的所有API接口的元数据。
  2. Api界面部署
    呈现由Swagger生成的API大概有两种方法(目前只找到两种)
  • Swagger自带有Swagger-UI可以直观显示API接口说明并可以在线调试
  • 使用Swagger2Markerup插件和AsciiDoc插件可以将Swagger生成的JSON元数据转换成HTML、PDF

注意:springfox-swagger2是依赖与Spring MVC框架的!!

Swagger-UI部署

  1. 引入Swagger-UI依赖


    io.springfox
    springfox-swagger-ui
    2.5.0

  1. 运行Spring项目,并访问htttp://[host]:[ip]/swagger-ui.html
Paste_Image.png

注意:必须首先部署Swagger

Swagger2Markerup与AsciiDoc插件部署

  1. maven插件部署

            
                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}
                        false
                    
                
                
                    
                        test
                        
                            convertSwagger2markup
                        
                    
                
            

            
            
                org.asciidoctor
                asciidoctor-maven-plugin
                1.5.3
                
                
                    
                        org.asciidoctor
                        asciidoctorj-pdf
                        1.5.0-alpha.10.1
                    
                
                
                
                    ${asciidoctor.input.directory}
                    index.adoc
                    
                        book
                        left
                        3
                        
                        
                        
                        
                        ${generated.asciidoc.directory}
                    
                
                
                
                    
                        output-html
                        test
                        
                            process-asciidoc
                        
                        
                            html5
                            ${asciidoctor.html.output.directory}
                        
                    
                       
                
            
  1. 编写文档生成脚本
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@AutoConfigureRestDocs(outputDir = "build/asciidoc/snippets")
@SpringBootTest(classes = {Application.class, SwaggerConfig.class})
@AutoConfigureMockMvc
public class Swagger2MarkupTest {
    private static final Logger LOG = LoggerFactory.getLogger(Swagger2MarkupTest.class);
    @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);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

注意上述的代码来源于swagger2markup官方说明一个demo,代码基于Spring Boot 1.4.0.release

Swagger2Markerup与AsciiDoc无插件调用(推荐)

有时候没有必须在maven的生命周期中远行单元测试生成文档,可以直接代码块生成文档

  1. maven依赖

 
         io.springfox
         springfox-swagger2
         2.5.0
         test
  

         io.github.swagger2markup
             swagger2markup-maven-plugin
              1.0.1
         test
         

 org.asciidoctor
     asciidoctorj
     1.5.4.1
     test

  1. 编写文档生成脚本
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {BIMobileMasterApplication.class, SwaggerConfig.class})
public class Swagger2MarkupTest {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    @Test
    public void convertSwaggerToAsciiDoc() throws Exception {
        MvcResult mvcResult = this.mockMvc.perform(get("/v2/api-docs")
                .accept("application/json;charset=utf-8"))
                .andExpect(status().isOk())
                .andReturn();

        //文档输出目录 
        String outputDirectory = "docs/restful/generated";
        Path outputDirectoryPath = Paths.get(outputDirectory); 
        MockHttpServletResponse response = mvcResult.getResponse();
        String swaggerJson = response.getContentAsString();
        swaggerJson = swaggerJson.replace("{\"status\":200,\"message\":\"\",\"data\":", "");
        swaggerJson = swaggerJson.substring(0,swaggerJson.length()-1);
        Swagger2MarkupConverter.from(swaggerJson)
        .build()
        .toFolder(outputDirectoryPath);
        
        Asciidoctor asciidoctor = Asciidoctor.Factory.create();
        Attributes attributes = new Attributes();
        attributes.setCopyCss(true);
        attributes.setLinkCss(false);
        attributes.setSectNumLevels(3);
        attributes.setAnchors(true);
        attributes.setSectionNumbers(true);
        attributes.setHardbreaks(true);
        attributes.setTableOfContents(Placement.LEFT);
        attributes.setAttribute("generated", "generated");
        OptionsBuilder optionsBuilder = OptionsBuilder.options()
                .backend("html5")
                .docType("book")
                .eruby("")
                .inPlace(true)
                .safe(SafeMode.UNSAFE)
                .attributes(attributes);
        String asciiInputFile = "docs/restful/index.adoc";
        asciidoctor.convertFile(
                new File(asciiInputFile),
                optionsBuilder.get());
        
    }

Swagger2Markup插件的说明及使用

swagger2markup-import-files-ext

插件说明

    有时在写接口注释时,可能需要对接口附加一些特别说明,这种情况下Swagger2的Java注释感觉有点鸡肋。 这时可以使用swagger2markup-import-files-ext动态向adoc文档添加额外内容。

插件使用

  1. maven插件部署
    swagger2markup-import-files-ext是可以与swagger2markup-maven-plugin插件一起使用的

                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}
                        false
                    
                
                
                    
                        test
                        
                            convertSwagger2markup
                        
                    
                
            

swagger2markup.extensions.dynamicOverview.contentPath
swagger2markup.extensions.dynamicDefinitions.contentPath
swagger2markup.extensions.dynamicPaths.contentPath
swagger2markup.extensions.dynamicSecurity.contentPath
四个参数是必须要指定的,意思是swagger2markup-maven-plugin启动时,会在上述四个目录中分别查找adoc,并把adoc内容分别插入到原adoc中。

  1. 创建自定义的adc
    完成步骤1部署后,我们需要定义每一个adoc的内容及其插入的位置。Swagger2Markup文档中定义下面规则用于将自定义的adoc插入到接口文档指定位置中:

All extensions, relatively to each extension contentPath :
DOCUMENT_BEFORE : document-before-.
DOCUMENT_BEGIN : document-begin-
.
DOCUMENT_END : document-end-.
DOCUMENT_AFTER : document-after-
.
Paths extensions, relatively to each extension contentPath :
OPERATION_BEFORE : /operation-before-.
OPERATION_BEGIN : /operation-begin-
.
OPERATION_END : /operation-end-.
OPERATION_AFTER : /operation-after-
.
OPERATION_DESCRIPTION_BEFORE: /operation-description-before-.
OPERATION_DESCRIPTION_BEGIN: /operation-description-begin-
.
OPERATION_DESCRIPTION_END: /operation-description-end-.
OPERATION_DESCRIPTION_AFTER: /operation-description-after-
.
OPERATION_PARAMETERS_BEFORE: /operation-parameters-before-.
OPERATION_PARAMETERS_BEGIN: /operation-parameters-begin-
.
OPERATION_PARAMETERS_END: /operation-parameters-end-.
OPERATION_PARAMETERS_AFTER: /operation-parameters-after-
.
OPERATION_RESPONSES_BEFORE: /operation-responses-before-.
OPERATION_RESPONSES_BEGIN: /operation-responses-begin-
.
OPERATION_RESPONSES_END: /operation-responses-end-.
OPERATION_RESPONSES_AFTER: /operation-responses-after-
.
OPERATION_SECURITY_BEFORE: /operation-security-before-.
OPERATION_SECURITY_BEGIN: /operation-security-begin
.
OPERATION_SECURITY_END: /operation-security-end-.
OPERATION_SECURITY_AFTER: /operation-security-after-
.
Definitions extensions, relatively to each extension contentPath :
DEFINITION_BEFORE : /definition-before-.
DEFINITION_BEGIN : /definition-begin-
.
DEFINITION_END : /definition-end-.
DEFINITION_AFTER : /definition-after-
.
Security extensions, relatively to each extension contentPath :
SECURITY_SCHEME_BEFORE : /security-scheme-before-.
SECURITY_SCHEME_BEGIN : /security-scheme-begin-
.
SECURITY_SCHEME_END : /security-scheme-end-.
SECURITY_SCHEME_AFTER : /security-scheme-after-
.

注意:operationId相当于@ApiOperation注释中的nickname属性

假设现在要将一段接口说明插入到operationId为findPet的接口中,我们需要做如下步骤:

  • 创建一个新目录src/docs/asciidoc/extensions/paths/
  • 在上述目录下创建一个findPet的子目录
  • 创建一个operation-before-test.adoc文件,内容为:
这是一个插件入adoc

可以看到生成文章后在在findPet的接口前面增加了一段描述


基于Spring的Restful接口生成工具_第1张图片
截图.png

其它的插入位置同理

swagger2markup-spring-restdocs-ext

插件说明

此插件可以自动将Spring rest doc生成的adoc片段添加到Paths说明部分的最后。
Spring Rest doc生成的adoc版片段:

  • curl-request.adoc
  • http-request.adoc
  • http-response.adoc
  • httpie-request.adoc

插件默认扫描前3个adoc,当然也可以通过withExplicitSnippets自定义扫描的文件名。

插件使用

  1. maven插件部署
    这里我们使用与上一个述件不同的部署方式,直接到通过代码配置插件
        
            io.github.swagger2markup
            swagger2markup-spring-restdocs-ext
            1.0.0
            compile
        
  1. 配置
        Map configMap = new HashMap<>(); 
        configMap.put("swagger2markup.extensions.springRestDocs.snippetBaseUri", "docs/restful/snippets");
        configMap.put("swagger2markup.extensions.springRestDocs.defaultSnippets", "true");
        configMap.put(Swagger2MarkupProperties.PATHS_GROUPED_BY, GroupBy.TAGS.name());
        Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder(configMap)

其中snippetBaseUri是Spring Rest Doc生成文档的位置;defaultSnippets指明是否使用默认的文件名扫描,如果设置为false,需要手工创建插件并通过withExplicitSnippets自行设置扫描的文件名。

其他参考资料

Spring Boot Test

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

你可能感兴趣的:(基于Spring的Restful接口生成工具)