Spring Boot 2.X(十五):整合 Swagger2 開發 API 文件(線上+離線)
前言
相信很多後端開發在專案中都會碰到要寫 api 文件,不管是給前端、移動端等提供更好的對接,還是以後為了以後交接方便,都會要求寫 api 文件。
而手寫 api 文件的話有諸多痛點:
- 文件更新的時候,需要再次傳送給對接人
- 介面太對,手寫文件很難管理
- 介面返回的結果不明確
- 不能直接線上測試介面,通常需要使用工具,如 postman 等
Swagger 就很好的解決了這個問題。
Swagger 簡介
Swagger 是一個規範和完整的框架,用於生成、描述、呼叫和視覺化 RESTful 風格的 Web 服務。總體目標是使客戶端和檔案系統作為伺服器以同樣的速度來更新。檔案的方法,引數和模型緊密整合到伺服器端的程式碼,允許API來始終保持同步。
官網:https://swagger.io
Swagger 使用
1.相關依賴
<!--swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
2.Swagger 配置類
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket buildDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInf()) //將api的元資訊設定為包含在json resourcelisting響應中 //.host("127.0.0.1:8080") //設定ip和埠,或者域名 .select() //啟動用於api選擇的生成器 //.apis(RequestHandlerSelectors.any()) .apis(RequestHandlerSelectors.basePackage("cn.zwqh.springboot.controller"))//指定controller路徑 .paths(PathSelectors.any()).build(); } private ApiInfo buildApiInf() { Contact contact=new Contact("朝霧輕寒","https://www.zwqh.top/","[email protected]"); return new ApiInfoBuilder() .title("Swagger Demo Restful API Docs")//文件標題 .description("Swagger 示例 Restful Api 文件")//文件描述 .contact(contact)//聯絡人 .version("1.0")//版本號 //.license("")//更新此API的許可證資訊 //.licenseUrl("")//更新此API的許可證Url //.termsOfServiceUrl("")//更新服務條款URL .build(); } }
3.Spring MVC 相關配置
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 靜態資源配置(預設)
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");// 靜態資源路徑
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
}
如果不新增此靜態資源配置會報錯,找不到相關路徑
4.Model 中使用 Swagger 註解
@ApiModel(value = "UserEntity", description = "使用者物件")
public class UserEntity implements Serializable{
/**
*
*/
private static final long serialVersionUID = 5237730257103305078L;
@ApiModelProperty(value ="使用者id",name="id",dataType="Long",required = false,example = "1",hidden = false )
private Long id;
@ApiModelProperty(value ="使用者名稱",name="userName",dataType="String",required = false,example = "關羽" )
private String userName;
@ApiModelProperty(value ="使用者性別",name="userSex",dataType="String",required = false,example = "男" )
private String userSex;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
}
5. Controller 中使用 Swagger 註解
@RestController
@RequestMapping("/api")
@Api(tags = { "介面分組1", "介面分組2" })
public class ApiController {
@Autowired
private UserDao userDao;
@GetMapping("/getAllUser")
@ApiOperation(value = "獲取所有使用者", notes = "", httpMethod = "GET", tags = "介面分組3")
public List<UserEntity> getAll() {
return userDao.getAll();
}
@GetMapping("/getUserById")
@ApiOperation(value = "根據id獲取使用者", notes = "id必傳", httpMethod = "GET")
@ApiImplicitParam(name = "id", value = "使用者id",example = "1", required = true, dataType = "long", paramType = "query")
public UserEntity getOne(Long id) {
return userDao.getOne(id);
}
@PostMapping("/getUserByNameAndSex")
@ApiOperation(value = "根據name和sex獲取使用者", notes = "", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(name = "userName", value = "使用者名稱", example = "關羽", required = true, dataType = "string", paramType = "query"),
@ApiImplicitParam(name = "userSex", value = "使用者性別", example = "男", required = true, dataType = "string", paramType = "query") })
public UserEntity getUserByNameAndSex(String userName, String userSex) {
return userDao.getUserByNameAndSex(userName, userSex);
}
@PostMapping("/insertUser")
@ApiOperation(value = "新增使用者", notes = "傳json,資料放body", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(name = "body", value = "使用者物件json", example = "{userName:'朝霧輕寒',userSex:'男'}", required = true) })
public String insertUser(@RequestBody String body) {
System.out.println(body);
UserEntity user = JSON.parseObject(body, UserEntity.class);
userDao.insertUser(user);
return "{code:0,msg:'success'}";
}
@PostMapping("/updateUser")
@ApiOperation(value = "修改使用者", notes = "傳json,資料放body", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(name = "body", value = "使用者物件json", example = "{id:23,userName:'朝霧輕寒',userSex:'女'}", required = true) })
public String updateUser(@RequestBody String body) {
System.out.println(body);
UserEntity user = JSON.parseObject(body, UserEntity.class);
userDao.updateUser(user);
return "{code:0,msg:'success'}";
}
@PostMapping("/deleteUser")
@ApiOperation(value = "刪除使用者", notes = "id必傳", httpMethod = "POST")
public String deleteUser(@ApiParam(name = "id", value = "使用者id", required = true) Long id) {
userDao.deleteUser(id);
return "{code:0,msg:'success'}";
}
}
5.測試
訪問 http://127.0.0.1:8080/swagger-ui.html 進行介面線上測試
Swagger 常用註解
1.@Api
用於類,表示標識這個類是swagger的資源。屬性如下:
- tags 表示說明,tags如果有多個值,會生成多個列表
- value 表示說明,可以使用tags替代
2.@ApiOperation
用於方法,表示一個http請求的操作。屬性如下:
- value 用於方法描述
- notes 用於提示內容
- tags 用於API文件控制的標記列表,視情況而用,可以進行獨立分組
3.@ApiParam
用於方法、引數、欄位說明;表示對引數的新增元資料。
- name 引數名
- value 引數說明
- required 是否必填
4.@ApiModel
用於類,表示對類進行說明,用於引數用實體類接受。
- value 物件名
- description 描述
5.@ApiModelProperty
用於方法、欄位,表示對model屬性的說明或者資料操作更改。
- value 欄位說明
- name 重寫屬性名
- dataType 重寫屬性資料型別
- required 是否必填
- example 舉例說明
- hidden 隱藏
6.@ApiIgnore
用於類、方法、方法引數,表示這個方法或者類被忽略,不在swagger-ui.html上顯示。
7.@ApiImplicitParam
用於方法,表示單獨的請求引數。
- name 引數名
- value 引數說明
- dataType 資料型別
- paramType 引數型別
- example 舉例說明
8.@ApiImplicitParams
用於方法,包含多個 @ApiImplicitParam。
9.@ApiResponses @ApiResponse
用於類或者方法,描述操作的可能響應。
- code 響應的HTTP狀態程式碼
- message 響應附帶的可讀訊息
10.@ResponseHeader
用於方法,響應頭設定。
- name 響應頭名稱
- description 頭描述
- response 預設響應類 void
- responseContainer 參考ApiOperation中配置
Swagger 匯出離線 api 文件
1.匯出 AsciiDocs、Markdown、Confluence 格式文件
新增依賴
<!-- swagger2markup 相關依賴 -->
<dependency>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup</artifactId>
<version>1.3.3</version>
</dependency>
轉換工具類
public class SwaggerUtils {
private static final String url = "http://127.0.0.1:8080/v2/api-docs";
/**
* 生成AsciiDocs格式文件
* @throws MalformedURLException
*/
public static void generateAsciiDocs() throws MalformedURLException {
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema().build();
Swagger2MarkupConverter.from(new URL(url))
.withConfig(config)
.build()
.toFolder(Paths.get("./docs/asciidoc/generated"));
}
/**
* 生成AsciiDocs格式文件,並彙總成一個檔案
* @throws MalformedURLException
*/
public static void generateAsciiDocsToFile() throws MalformedURLException {
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL(url))
.withConfig(config)
.build()
.toFile(Paths.get("./docs/asciidoc/generated/all"));
}
/**
* 生成Markdown格式文件
* @throws MalformedURLException
*/
public static void generateMarkdownDocs() throws MalformedURLException {
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.MARKDOWN)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL(url))
.withConfig(config)
.build()
.toFolder(Paths.get("./docs/markdown/generated"));
}
/**
* 生成Markdown格式文件,並彙總成一個檔案
* @throws MalformedURLException
*/
public static void generateMarkdownDocsToFile() throws MalformedURLException {
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.MARKDOWN)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL(url))
.withConfig(config)
.build()
.toFile(Paths.get("./docs/markdown/generated/all"));
}
/**
* 生成Confluence格式文件
* @throws MalformedURLException
*/
public static void generateConfluenceDocs() throws MalformedURLException {
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.CONFLUENCE_MARKUP)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL(url))
.withConfig(config)
.build()
.toFolder(Paths.get("./docs/confluence/generated"));
}
/**
* 生成Confluence格式文件,並彙總成一個檔案
* @throws MalformedURLException
*/
public static void generateConfluenceDocsToFile() throws MalformedURLException {
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.CONFLUENCE_MARKUP)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL(url))
.withConfig(config)
.build()
.toFile(Paths.get("./docs/confluence/generated/all"));
}
}
使用測試 Controller
@RestController
@RequestMapping("/export")
@ApiIgnore
public class ExportController {
@RequestMapping("/ascii")
public String exportAscii() throws MalformedURLException{
SwaggerUtils.generateAsciiDocs();
return "success";
}
@RequestMapping("/asciiToFile")
public String asciiToFile() throws MalformedURLException{
SwaggerUtils.generateAsciiDocsToFile();
return "success";
}
@RequestMapping("/markdown")
public String exportMarkdown() throws MalformedURLException{
SwaggerUtils.generateMarkdownDocs();
return "success";
}
@RequestMapping("/markdownToFile")
public String exportMarkdownToFile() throws MalformedURLException{
SwaggerUtils.generateMarkdownDocsToFile();
return "success";
}
@RequestMapping("/confluence")
public String confluence() throws MalformedURLException{
SwaggerUtils.generateConfluenceDocs();
return "success";
}
@RequestMapping("/confluenceToFile")
public String confluenceToFile() throws MalformedURLException{
SwaggerUtils.generateConfluenceDocsToFile();
return "success";
}
}
2.匯出 html、pdf、xml 格式
新增依賴
<!--離線文件 -->
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<!--springfox-staticdocs 生成靜態文件 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-staticdocs</artifactId>
<version>2.6.1</version>
</dependency>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup-maven-plugin</artifactId>
<version>1.3.1</version>
<configuration>
<swaggerInput>http://127.0.0.1:8080/v2/api-docs</swaggerInput>
<outputDir>./docs/asciidoc/generated</outputDir>
<config>
<swagger2markup.markupLanguage>ASCIIDOC</swagger2markup.markupLanguage>
</config>
</configuration>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<!-- <version>2.0.0-RC.1</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>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
<configuration>
<sourceDirectory>./docs/asciidoc/generated</sourceDirectory>
<outputDirectory>./docs/asciidoc/html</outputDirectory>
<backend>html</backend>
<!-- <outputDirectory>./docs/asciidoc/pdf</outputDirectory>
<backend>pdf</backend> -->
<headerFooter>true</headerFooter>
<doctype>book</doctype>
<sourceHighlighter>coderay</sourceHighlighter>
<attributes>
<!-- 選單欄在左邊 -->
<toc>left</toc>
<!-- 多標題排列 -->
<toclevels>3</toclevels>
<!-- 自動打數字序號 -->
<sectnums>true</sectnums>
</attributes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
可以修改此處 html 和 pdf,通過 mvn asciidoctor:process-asciidoc 可以匯出相應格式檔案
<outputDirectory>./docs/asciidoc/html</outputDirectory>
<backend>html</backend>
執行 mvn asciidoctor:process-asciidoc 後再執行 mvn generate-resources,可在 targt/generated-docs 目錄下生成 xml 格式檔案。
完整程式碼
github
碼