基於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部署說明
- 在pom引入下面依賴:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
- 配置一個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.<GrantType>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", "test@test.de"))
.license("Apache 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.version("1.0.0")
.build();
}
}
- 執行Spring專案
執行專案後會釋出Spring的路由器多了若干個對外的介面,其中一個是/v2/api-docs/
。
通過這個介面可以獲取到由Swagger生成的所有API介面的後設資料。 - Api介面部署
呈現由Swagger生成的API大概有兩種方法(目前只找到兩種)
- Swagger自帶有Swagger-UI可以直觀顯示API介面說明並可以線上除錯
- 使用Swagger2Markerup外掛和AsciiDoc外掛可以將Swagger生成的JSON後設資料轉換成HTML、PDF
注意:springfox-swagger2是依賴與Spring MVC框架的!!
Swagger-UI部署
- 引入Swagger-UI依賴
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.5.0</version>
</dependency>
- 執行Spring專案,並訪問htttp://[host]:[ip]/swagger-ui.html
注意:必須首先部署Swagger
Swagger2Markerup與AsciiDoc外掛部署
- maven外掛部署
<!-- First, use the swagger2markup plugin to generate asciidoc -->
<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/docs/asciidoc/extensions/overview</swagger2markup.extensions.dynamicOverview.contentPath>
<swagger2markup.extensions.dynamicDefinitions.contentPath>${project.basedir}/src/docs/asciidoc/extensions/definitions</swagger2markup.extensions.dynamicDefinitions.contentPath>
<swagger2markup.extensions.dynamicPaths.contentPath>${project.basedir}/src/docs/asciidoc/extensions/paths</swagger2markup.extensions.dynamicPaths.contentPath>
<swagger2markup.extensions.dynamicSecurity.contentPath>${project.basedir}src/docs/asciidoc/extensions/security/</swagger2markup.extensions.dynamicSecurity.contentPath>
<swagger2markup.extensions.springRestDocs.snippetBaseUri>${swagger.snippetOutput.dir}</swagger2markup.extensions.springRestDocs.snippetBaseUri>
<swagger2markup.extensions.springRestDocs.defaultSnippets>false</swagger2markup.extensions.springRestDocs.defaultSnippets>
</config>
</configuration>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>convertSwagger2markup</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run the generated asciidoc through Asciidoctor to generate
other documentation types, such as PDFs or HTML5 -->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<!-- Include Asciidoctor PDF for pdf generation -->
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>1.5.0-alpha.10.1</version>
</dependency>
</dependencies>
<!-- Configure generic document generation settings -->
<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>
<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>
<!--
<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>
- 編寫文件生成指令碼
@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的生命週期中遠行單元測試生成文件,可以直接程式碼塊生成文件
- maven依賴
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup-maven-plugin</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId>
<version>1.5.4.1</version>
<scope>test</scope>
</dependency>
- 編寫文件生成指令碼
@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文件新增額外內容。
外掛使用
- maven外掛部署
swagger2markup-import-files-ext是可以與swagger2markup-maven-plugin外掛一起使用的
<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/docs/asciidoc/extensions/overview</swagger2markup.extensions.dynamicOverview.contentPath>
<swagger2markup.extensions.dynamicDefinitions.contentPath>${project.basedir}/src/docs/asciidoc/extensions/definitions</swagger2markup.extensions.dynamicDefinitions.contentPath>
<swagger2markup.extensions.dynamicPaths.contentPath>${project.basedir}/src/docs/asciidoc/extensions/paths</swagger2markup.extensions.dynamicPaths.contentPath>
<swagger2markup.extensions.dynamicSecurity.contentPath>${project.basedir}src/docs/asciidoc/extensions/security/</swagger2markup.extensions.dynamicSecurity.contentPath>
<swagger2markup.extensions.springRestDocs.snippetBaseUri>${swagger.snippetOutput.dir}</swagger2markup.extensions.springRestDocs.snippetBaseUri>
<swagger2markup.extensions.springRestDocs.defaultSnippets>false</swagger2markup.extensions.springRestDocs.defaultSnippets>
</config>
</configuration>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>convertSwagger2markup</goal>
</goals>
</execution>
</executions>
</plugin>
swagger2markup.extensions.dynamicOverview.contentPath
swagger2markup.extensions.dynamicDefinitions.contentPath
swagger2markup.extensions.dynamicPaths.contentPath
swagger2markup.extensions.dynamicSecurity.contentPath
四個引數是必須要指定的,意思是swagger2markup-maven-plugin啟動時,會在上述四個目錄中分別查詢adoc,並把adoc內容分別插入到原adoc中。
- 建立自定義的adc
完成步驟1部署後,我們需要定義每一個adoc的內容及其插入的位置。Swagger2Markup文件中定義下面規則用於將自定義的adoc插入到介面文件指定位置中:
All extensions, relatively to each extension contentPath :
DOCUMENT_BEFORE : document-before-.<markup.ext>
DOCUMENT_BEGIN : document-begin-.<markup.ext>
DOCUMENT_END : document-end-.<markup.ext>
DOCUMENT_AFTER : document-after-.<markup.ext>
Paths extensions, relatively to each extension contentPath :
OPERATION_BEFORE : <operationId>/operation-before-.<markup.ext>
OPERATION_BEGIN : <operationId>/operation-begin-.<markup.ext>
OPERATION_END : <operationId>/operation-end-.<markup.ext>
OPERATION_AFTER : <operationId>/operation-after-.<markup.ext>
OPERATION_DESCRIPTION_BEFORE: <operationId>/operation-description-before-.<markup.ext>
OPERATION_DESCRIPTION_BEGIN: <operationId>/operation-description-begin-.<markup.ext>
OPERATION_DESCRIPTION_END: <operationId>/operation-description-end-.<markup.ext>
OPERATION_DESCRIPTION_AFTER: <operationId>/operation-description-after-.<markup.ext>
OPERATION_PARAMETERS_BEFORE: <operationId>/operation-parameters-before-.<markup.ext>
OPERATION_PARAMETERS_BEGIN: <operationId>/operation-parameters-begin-.<markup.ext>
OPERATION_PARAMETERS_END: <operationId>/operation-parameters-end-.<markup.ext>
OPERATION_PARAMETERS_AFTER: <operationId>/operation-parameters-after-.<markup.ext>
OPERATION_RESPONSES_BEFORE: <operationId>/operation-responses-before-.<markup.ext>
OPERATION_RESPONSES_BEGIN: <operationId>/operation-responses-begin-.<markup.ext>
OPERATION_RESPONSES_END: <operationId>/operation-responses-end-.<markup.ext>
OPERATION_RESPONSES_AFTER: <operationId>/operation-responses-after-.<markup.ext>
OPERATION_SECURITY_BEFORE: <operationId>/operation-security-before-.<markup.ext>
OPERATION_SECURITY_BEGIN: <operationId>/operation-security-begin.<markup.ext>
OPERATION_SECURITY_END: <operationId>/operation-security-end-.<markup.ext>
OPERATION_SECURITY_AFTER: <operationId>/operation-security-after-.<markup.ext>
Definitions extensions, relatively to each extension contentPath :
DEFINITION_BEFORE : <definitionName>/definition-before-.<markup.ext>
DEFINITION_BEGIN : <definitionName>/definition-begin-.<markup.ext>
DEFINITION_END : <definitionName>/definition-end-.<markup.ext>
DEFINITION_AFTER : <definitionName>/definition-after-.<markup.ext>
Security extensions, relatively to each extension contentPath :
SECURITY_SCHEME_BEFORE : <securitySchemeName>/security-scheme-before-.<markup.ext>
SECURITY_SCHEME_BEGIN : <securitySchemeName>/security-scheme-begin-.<markup.ext>
SECURITY_SCHEME_END : <securitySchemeName>/security-scheme-end-.<markup.ext>
SECURITY_SCHEME_AFTER : <securitySchemeName>/security-scheme-after-.<markup.ext>
注意:operationId相當於@ApiOperation註釋中的nickname屬性
假設現在要將一段介面說明插入到operationId為findPet的介面中,我們需要做如下步驟:
- 建立一個新目錄src/docs/asciidoc/extensions/paths/
- 在上述目錄下建立一個
findPet
的子目錄 - 建立一個operation-before-test.adoc檔案,內容為:
這是一個外掛入adoc
可以看到生成文章後在在findPet的介面前面增加了一段描述
其它的插入位置同理
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自定義掃描的檔名。
外掛使用
- maven外掛部署
這裡我們使用與上一個述件不同的部署方式,直接到通過程式碼配置外掛
<dependency>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup-spring-restdocs-ext</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
- 配置
Map<String, String> 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
相關文章
- 基於工程經驗的『RESTful介面設計規範』REST
- 基於 NodeGit 的週報生成工具Git
- 基於 Spring Boot 2.0 構建一個 RESTful WebServiceSpring BootRESTWeb
- 無需程式設計,基於PostgreSQL零程式碼生成CRUD增刪改查RESTful API介面程式設計SQLRESTAPI
- 介面文件生成工具
- 基於LangChain手工測試用例轉介面自動化測試生成工具LangChain
- 基於Spring-AOP的自定義分片工具Spring
- 基於 Django 的 Dubbo 介面測試工具平臺Django
- Spring Boot入門系列(二十)快速打造Restful API 介面Spring BootRESTAPI
- Spring boot+Swagger配置無侵入式Restful介面(二)Spring BootSwaggerREST
- Apache Isis:基於領域驅動自動生成SpringBoot介面的快速CRUD開發工具ApacheSpring Boot
- 基於多資料來源零程式碼同時生成多個資料庫CRUD增刪改查RESTful API介面資料庫RESTAPI
- 基於.NetCore開發部落格專案 StarBlog - (21) 開始開發RESTFul介面NetCoreREST
- python實戰-基於正交實驗(工具:allpairs)自動生成介面異常測試用例PythonAI
- Spring Cloud雲架構-Restful 基礎架構SpringCloud架構REST
- 介面開發-restfulREST
- Restful介面規約REST
- SpringBoot Restful 介面實現Spring BootREST
- 基於Spring Batch的Spring Boot的教程 - BaeldungBATSpring Boot
- Bing每日桌布的RESTful介面實現REST
- Spring Security 實戰乾貨:基於配置的介面角色訪問控制Spring
- 基於滴滴雲搭建輕量文件網站生成工具 Docsify網站
- drf : web應用模式,RESTful API規範,介面測試工具:PostmanWeb模式RESTAPIPostman
- spring-boot-route(五)整合Swagger生成介面文件SpringbootSwagger
- spring-boot-route(六)整合JApiDocs生成介面文件SpringbootAPI
- Spring基於XML方式的使用SpringXML
- 基於Maven的Spring整合CXFMavenSpring
- Spring Security 實戰乾貨:基於註解的介面角色訪問控制Spring
- Spring Boot中基於HTML發票/收據生成和下載功能Spring BootHTML
- 關於Spring生命週期控制的介面:SmartLifecycleSpring
- 一個基於GPT模型實現的Git Commit資訊自動生成工具GPT模型GitMIT
- 基於MySql和Sails.js的RESTful風格的api實現MySqlAIJSRESTAPI
- 基於canvas生成海報Canvas
- 基於canvas生成圖片Canvas
- Spring專案如何優雅的生成介面文件與客戶端Spring客戶端
- Presto通過RESTful介面新增ConnectorREST
- 個人寫的一個小工具 laravel生成介面文件Laravel
- Spring基於註解的aop配置Spring
- spring boot基於Java的容器配置Spring BootJava