1. 程式人生 > >Spring Boot 2.X(十五):整合 Swagger2 開發 API 文件(線上+離線)

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