java集成開發SpringBoot生成接口文檔示例實現

大傢好,我是飄渺。

SpringBoot老鳥系列的文章已經寫瞭兩篇,每篇的閱讀反響都還不錯,果然大傢還是對SpringBoot比較感興趣。那今天我們就帶來老鳥系列的第三篇:集成Swagger接口文檔以及Swagger的高級功能。 文章涉及到的代碼已經上傳到瞭github,希望最終能應用在你們實際項目上,當然如果有其他需要我添加到內容也可以直接留言告訴我,我會視情況給你們加上去的。

SpringBoot 如何統一後端返回格式?老鳥們都是這樣玩的!

SpringBoot 如何進行參數校驗? 老鳥們都是這樣玩的!

好瞭,閑話少敘,讓我們先來看看為什麼要用Swagger?

為什麼要用Swagger ?

作為一名程序員,我們最討厭兩件事:1. 別人不寫註釋。2. 自己寫註釋。

而作為一名接口開發者,我們同樣討厭兩件事:

1. 別人不寫接口文檔,文檔不及時更新。

2. 需要自己寫接口文檔,還需要及時更新。

相信無論是前端還是後端開發,都或多或少地被接口文檔折磨過。前端經常抱怨後端給的接口文檔與實際情況不一致。後端又覺得編寫及維護接口文檔會耗費不少精力,經常來不及更新。

而隨著Springboot、Springcloud等微服務的流行,每個項目都有成百上千個接口調用,這時候再要求人工編寫接口文檔並且保證文檔的實時更新幾乎是一件不可能完成的事,所以這時候我們迫切需要一個工具,一個能幫我們自動化生成接口文檔以及自動更新文檔的工具。它就是Swagger。

Swagger 提供瞭一個全新的維護 API 文檔的方式,有4大優點:

自動生成文檔:隻需要少量的註解,Swagger 就可以根據代碼自動生成 API 文檔,很好的保證瞭文檔的時效性。

跨語言性,支持 40 多種語言。

Swagger UI 呈現出來的是一份可交互式的 API 文檔,我們可以直接在文檔頁面嘗試 API 的調用,省去瞭準備復雜的調用參數的過程。

還可以將文檔規范導入相關的工具(例如 SoapUI), 這些工具將會為我們自動地創建自動化測試。

現在我們知道瞭Swagger的作用,接下來將其集成到我們項目中。

Swagger集成

集成Swagger很簡單,隻需要簡單三步。

第一步: 引入依賴包

<!--swagger-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

<!--swagger-ui-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

第二步:修改配置文件

application.properties 加入配置

# 用於控制是否開啟Swagger,生產環境記得關閉Swagger,將值設置為 false
springfox.swagger2.enabled = true

增加一個swagger配置類

@Configuration
@EnableSwagger2
@ConditionalOnClass(Docket.class)
public class SwaggerConfig {
  
    private static final String VERSION = "1.0";

    @Value("${springfox.swagger2.enabled}")
    private Boolean swaggerEnabled;

    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                .enable(swaggerEnabled)
                .groupName("SwaggerDemo")
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .paths(PathSelectors.any())
                .build();
    }

    /**
     * 添加摘要信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("接口文檔")
                .contact(new Contact("JAVA日知錄","http://javadaily.cn","[email protected]"))
                .description("Swagger接口文檔")
                .version(VERSION)
                .build();
    }

}

這裡通過 .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))

表面給加上 @Api註解的類自動生成接口文檔。

第三步,配置API接口

@RestController
@Api(tags = "參數校驗")
@Slf4j
@Validated
public class ValidController {

    @PostMapping("/valid/test1")
    @ApiOperation("RequestBody校驗")
    public String test1(@Validated @RequestBody ValidVO validVO){
        log.info("validEntity is {}", validVO);
        return "test1 valid success";
    }

    @ApiOperation("Form校驗")
    @PostMapping(value = "/valid/test2")
    public String test2(@Validated ValidVO validVO){
        log.info("validEntity is {}", validVO);
        return "test2 valid success";
    }

    @ApiOperation("單參數校驗")
    @PostMapping(value = "/valid/test3")
    public String test3(@Email String email){
        log.info("email is {}", email);
        return "email valid success";
    }
}

通過 @Api註解標註需要生成接口文檔,通過 @ApiOperation註解標註接口名。

同時我們給 ValidVO也加上對應的註解

@Data
@ApiModel(value = "參數校驗類")
public class ValidVO {

    @ApiModelProperty("ID")
    private String id;

    @ApiModelProperty(value = "應用ID",example = "cloud")
    private String appId;

    @NotEmpty(message = "級別不能為空")
    @ApiModelProperty(value = "級別")
    private String level;

    @ApiModelProperty(value = "年齡")
    private int age;
  
    ...

}

通過 @ApiModel標註這是一個參數實體,通過 @ApiModelProperty標註字段說明。

Unable to infer base url

簡單三步,我們項目就集成瞭Swagger接口文檔,趕緊啟動服務,訪問 http://localhost:8080/swagger-ui.html 體驗一下。

image-20210820155105249

好吧,出瞭點小問題,不過不用慌。

出現這個問題的原因是因為我們加上瞭 ResponseBodyAdvice統一處理返回值/響應體,導致給Swagger的返回值也包裝瞭一層,UI頁面無法解析。可以通過 http://localhost:8080/v2/api-docs?group=SwaggerDemo觀察Swagger返回的json數據。

image-20210820165255112

既然知道瞭問題原因那就很好解決瞭,我們隻需要在ResponseBodyAdvice處理類中隻轉換我們自己項目的接口即可。

@RestControllerAdvice(basePackages = "com.jianzh5.blog")
@Slf4j
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
  ...
}   

通過添加basePackage屬性限定統一返回值的范圍,這樣就不影響Swagger瞭。

重啟服務器再次訪問swagger接口地址,就可以看到接口文檔頁面瞭。

image-20210820170634932

For input string: “”

Swagger2.9.2有個bug,就是當我們參數實體有int類型的參數時,打開Swagger接口頁面時後端會一直提示異常:

java.lang.NumberFormatException: For input string: ""
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.base/java.lang.Long.parseLong(Long.java:702)
	at java.base/java.lang.Long.valueOf(Long.java:1144)

有兩種解決方案:

給int類型的字段使用@ApiModelPorperty註解時添加example屬性

@ApiModelProperty(value = "年齡",example = "10")
private int age;

去除原swagger中的swagger-modelsswagger-annotations,自行引入高版本的annotations和models

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>2.9.2</version>
  <exclusions>
    <exclusion>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-annotations</artifactId>
    </exclusion>
    <exclusion>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-models</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-annotations</artifactId>
  <version>1.5.22</version>
</dependency>
<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-models</artifactId>
  <version>1.5.22</version>
</dependency>

集成Swagger過程中雖然會出現兩個小問題,解決後我們就可以愉快享受Swagger給我們帶來的便利瞭。

Swagger美化

Swagger原生UI有點醜,我們可以借助Swagger的增強工具 knife4j優化一下。

第一步: 引入依賴包

 <!--整合Knife4j-->
<dependency>
  <groupId>com.github.xiaoymin</groupId>
  <artifactId>knife4j-spring-boot-starter</artifactId>
  <version>2.0.4</version>
</dependency>

由於knife4j中已經帶瞭 swagger-annotations和 swagger-models的依賴,所以我們可以把上文中手動添加的兩個依賴刪除。

第二步:啟用knife4j增強

@Configuration
@EnableSwagger2
@ConditionalOnClass(Docket.class)
@EnableKnife4j
public class SwaggerConfig {
  ...
}

通過上面兩步我們就完成瞭Swagger的美化,通過瀏覽器訪問 http://localhost:8080/doc.html 即可看到效果。

image-20210822191542202

Swagger參數分組

看到這裡的同學心理肯定會想,就這?這就是老鳥的做法?跟我們新手也沒啥區別呀

別急,我們先來看一個效果。

首先我們定義瞭兩個接口,一個新增,一個編輯

@ApiOperation("新增")
@PostMapping(value = "/valid/add")
public String add(@Validated(value = {ValidGroup.Crud.Create.class}) ValidVO validVO){
  log.info("validEntity is {}", validVO);
  return "test3 valid success";
}

@ApiOperation("更新")
@PostMapping(value = "/valid/update")
public String update(@Validated(value = ValidGroup.Crud.Update.class) ValidVO validVO){
  log.info("validEntity is {}", validVO);
  return "test4 valid success";
}

註意看,這裡用的是同一個實體 ValidVO來接收前端參數,隻不過使用瞭參數校驗中的分組,然後我們打開kife4j頁面觀察兩者的接口文檔有何不同。

新增:

image-20210822224030743

編輯:

image-20210822224106495

通過上面可以看到,雖然用於接受參數的實體一樣,但是當分組不一樣時展示給前端的參數也不一樣,這就是Swagger的分組功能。

當然原生的Swagger是不支持分組功能的,我們需要對Swagger進行擴展。我已經將代碼上傳到瞭github上,由於代碼量比較多這裡就不展示瞭,大傢可以自行查閱。

image-20210822224422361

引入擴展類後還需要在Swagger配置類 SwaggerConfig中註入對應的Bean。

@Configuration
@EnableSwagger2
@ConditionalOnClass(Docket.class)
@EnableKnife4j
public class SwaggerConfig {
    ...

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public GroupOperationModelsProviderPlugin groupOperationModelsProviderPlugin() {
        return new GroupOperationModelsProviderPlugin();
    }

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public GroupModelBuilderPlugin groupModelBuilderPlugin() {
        return new GroupModelBuilderPlugin();
    }

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public GroupModelPropertyBuilderPlugin groupModelPropertyBuilderPlugin() {
        return new GroupModelPropertyBuilderPlugin();
    }

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public GroupExpandedParameterBuilderPlugin groupExpandedParameterBuilderPlugin() {
        return new GroupExpandedParameterBuilderPlugin();
    }

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public GroupOperationBuilderPlugin groupOperationBuilderPlugin() {
        return new GroupOperationBuilderPlugin();
    }

    @Bean
    @Primary
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public GroupModelAttributeParameterExpander groupModelAttributeParameterExpander(FieldProvider fields, AccessorsProvider accessors, EnumTypeDeterminer enumTypeDeterminer) {
        return new GroupModelAttributeParameterExpander(fields, accessors, enumTypeDeterminer);
    }


}

分組使用說明

1.在bean對象的屬性裡配置如下註釋

@Null(groups = ValidGroup.Crud.Create.class)
@NotNull(groups = ValidGroup.Crud.Update.class,message = "應用ID不能為空")
@ApiModelProperty(value = "應用ID",example = "cloud")
private String appId;

當新增場景的時候,appId為空,不需要傳值; 當修改場景的時候,appId不能為空,需要傳值 ;其他沒有配置組的皆為默認組(Default)

2.在接口參數的時候加入組規則校驗

 @ApiOperation("新增")
 @PostMapping(value = "/valid/add")
 public String add(@Validated(value = {ValidGroup.Crud.Create.class}) ValidVO validVO){
 	log.info("validEntity is {}", validVO);
 	return "test3 valid success";
 }

當前接口會針對默認組的bean屬性進行校驗,同時針對保存常見的屬性進行校驗。

小結

Swagger集成相對來說還是很簡單的,雖然在集成過程中也出現瞭幾個小問題,不過也很容易就解決瞭。今天文章的重點內容是Swagger分組功能,跟之前的參數校驗文章一樣,很多同學遇到這種分組場景時往往會選擇創建多個實體類,雖然也能解決問題,隻不過總是有點別扭。

不過遺憾的是,本文中Swagger的分組擴展隻支持Swagger2,至於新版本Swagger3就不怎麼支持瞭。如果有同學已經擴展好瞭,歡迎給我提pr呀。

以上就是java集成開發SpringBoot生成接口文檔示例實現的詳細內容,更多關於SpringBoot生成接口文檔的資料請關註WalkonNet其它相關文章!

推薦閱讀: