SpringBoot第十一集:整合Swagger3.0-定制RESTful與統一接口返回值(2021最新最易懂)


SpringBoot第十一集:整合Swagger3.0-定制RESTful與統一接口返回值(2021最新最易懂)

一,整合Swagger3.0

  隨着Spring Boot、Spring Cloud等微服務的流行,在微服務的設計下,小公司微服務工程jar小的幾十個,大公司大的工程拆分jar多則幾百上萬個,這么多的微服務必定產生了大量的接口調用。而接口的調用就必定要寫接口文檔(由開發人員編寫)。

  存在的問題:(面對多個開發人員或多個開發團隊)

  1. 項目開發接口眾多,細節,復雜,且多樣化,高質量地創建接口文檔費時,費力。
  2. 隨着項目的進行,不可避免整改和優化,需要不斷的修改接口實現,伴隨着也需要同時修改接口文檔,管理不方便不說,還容易出現不一致的情況。

概述

  Swagger 是一個規范和完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。

  實際開發過程中Swagger 能夠完美的與Spring Boot程序整合,組織出強大RESTful API文檔,它既可以減少我們創建文檔的工作量,同時也整合了說明內容在實現代碼中,讓維護文檔和修改代碼融為一體,可以讓我們在修改代碼邏輯的同時方便的修改文檔說明。另外Swagger2還提供了強大的頁面測試功能,讓開發者能快速的調試每個RESTful API。

1.整合實現

1,引入pom依賴。

  Swagger3.0的更新還是有很大變化的(詳情參考),首先在依賴jar問題上,它新增了springfox-boot-starter,修復了2.x版本的沖突,移除了guava。另外Swagger3.0還移除了注解@EnableSwagger2,增加注解@EnableOpenApi。

 <!-- SpringBoot整合springfox-swagger3 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>
        

  啟動SpringBoot主程序,可以直接測試訪問:

  測試地址:http://localhost:8080/swagger-ui/index.html   (訪問后提供的有默認的錯誤調用接口文檔)

  需要注意的是,Swagger3.0還更新了UI頁面地址,如上,而Swagger2.x的訪問地址是這樣的:http://localhost:8080/swagger-ui.html

  

2,自定義SwaggerConfig類

  新增SwaggerConfig類,並將其加載到Spring IOC中。需要注意:自定義Swagger配置類,Swagger3.0移除注解@EnableSwagger2,增加注解@EnableOpenApi。@EnableOpenApi可以在Config類中應用,也可以在SpringBoot主啟動類上使用(選其一即可),表示啟用自定義API接口。

 1 @EnableOpenApi   // 開啟Swagger自定義接口文檔
 2 @Configuration   // 相當於Spring配置中的<beans>
 3 public class SwaggerConfig {
 4     @Bean   // 相當於Spring 配置中的<bean>
 5     public Docket createRestApi() {
 6         return new Docket(DocumentationType.OAS_30)
 7                 .apiInfo(apiInfo())
 8                 .select()
 9                 .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
10                 .paths(PathSelectors.any())
11                 .build();
12     }
13     // API基礎信息定義(就是更新Swagger默認頁面上的信息)
14     private ApiInfo apiInfo() {
15         return new ApiInfoBuilder()
16                 .title("Swagger3接口文檔測試")
17                 .description("文檔描述:更多問題,請聯系開發者")
18                 .contact(new Contact("xsge123(name)", "作者網站(url)", "1511868921@qq.com(email)"))
19                 .version("1.0")
20                 .build();
21     }
22     
23 }

3,編寫Controller提供RESTful風格的接口API

編寫Controller前,先看看一些注解的意思吧!

@Api:用在控制器類上,表示對類的說明
tags="說明該類的作用,可以在UI界面上看到的說明信息的一個好用注解" value="該參數沒什么意義,在UI界面上也看到,所以不需要配置" @ApiOperation:用在請求的方法上,說明方法的用途、作用 value="說明方法的用途、作用" notes="方法的備注說明" @ApiImplicitParams:用在請求的方法上,表示一組參數說明 @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一個請求參數的各個方面(標注一個指定的參數,詳細概括參數的各個方面,例如:參數名是什么?參數意義,是否必填等) name:屬性值為方法參數名 value:參數意義的漢字說明、解釋 required:參數是否必須傳 paramType:參數放在哪個地方 · header --> 請求參數的獲取:@RequestHeader · query --> 請求參數的獲取:@RequestParam · path(用於restful接口)--> 請求參數的獲取:@PathVariable · div(不常用) · form(不常用) dataType:參數類型,默認String,其它值dataType="Integer" defaultValue:參數的默認值 @ApiResponses:用在請求的方法上,表示一組響應 @ApiResponse:用在@ApiResponses中,一般用於表達一個錯誤的響應信息 code:狀態碼數字,例如400 message:信息,例如"請求參數沒填好" response:拋出異常的類 @ApiModel:用於響應類上(POJO實體類),描述一個返回響應數據的信息(描述POJO類請求或響應的實體說明) (這種一般用在post接口的時候,使用@RequestBody接收JSON格式的數據的場景,請求參數無法使用@ApiImplicitParam注解進行描述的時候) @ApiModelProperty:用在POJO屬性上,描述響應類的屬性說明
@ApiIgnore:使用該注解忽略這個API;

Spring Boot中包含了一些控制器方法RESTful接口注解,對應於HTTP協議中的方法:

  • @GetMapping對應HTTP中的GET方法;

  • @PostMapping對應HTTP中的POST方法;

  • @PutMapping對應HTTP中的PUT方法;

  • @DeleteMapping對應HTTP中的DELETE方法;

  • @PatchMapping對應HTTP中的PATCH方法。

 1 @Api(value = "測試SwaggerAPI Annotation", tags = "Swagger測試之用戶信息管理API")
 2 @RestController
 3 @RequestMapping("/user")
 4 public class SwaggerController {
 5     
 6     @ApiIgnore    // 忽略這個API
 7     @GetMapping("/hello")
 8     public String hello() {
 9         return "hello";
10     }
11     
12     @GetMapping(value = "/swaggerGet/{name}")
13     @ApiOperation(value = "接口方法說明", notes = "接口的詳情描述")
14     @ApiImplicitParam(name = "name", value = "請傳遞一個用戶名參數",required = true, dataType = "String", paramType = "path")
15     public String swaggerGet(@PathVariable  String name) {
16         return "name="+name;
17     }
18     
19     @PostMapping(value = "/swaggerPost")
20     @ApiOperation(value = "新增用戶", notes = "Swagger測試RESTful之POST請求測試入參一個POJO(JSON格式)")
21     public User swaggerGet(@RequestBody User user) {
22         return user;
23     }
24     
25 }

實體類

 1 @ApiModel("用戶信息實體類")
 2 @Data
 3 public class User {
 4     // example:示例代碼值
 5     @ApiModelProperty(value = "用戶名",dataType="String",name="username",example="xsge")
 6     private String username;
 7     @ApiModelProperty(value = "賬戶密碼",dataType="String",name="password",example="123456")
 8     private String password;
 9     
10 }

4,啟動SpringBoot工程,測試訪問

  輸入地址:http://localhost:8080/swagger-ui/index.html 進入Swagger接口文檔界面。

  注意:Swagger2.x版本不一樣哦!關於2.x的配置版本也有些不同,這里就不介紹了...

 

二,統一接口返回值

  我們在應用中經常會涉及到 server 和 client 的交互,目前比較流行的是基於 json 格式的數據交互。但是 json 只是消息的格式,其中的內容還需要我們自行設計。不管是 HTTP 接口還是 RPC 接口保持返回值格式統一很重要,這將大大降低 client 的開發成本。

一般定義Response的標准格式包含四部分:

  • Integer  code ;成功時返回 0 ,失敗時返回具體錯誤碼。(可以自定義錯誤碼,使用枚舉類封裝)
  • String  message ;成功時返回 null ,失敗時返回具體錯誤消息。(可以自定義錯誤消息,使用枚舉類封裝)
  • T  data ;成功時具體返回值,失敗時為 null 。

例如:

1 {
2     "code": 0,
3     "messages": "",
4     "data": ""
5 }

1.定義枚舉類,封裝狀態碼和消息

  定義一些常見的成功與失敗的枚舉常量。如下:(該枚舉類可以作為工具使用了,有心的朋友可自行保存一下)

 1 public enum EnumCode {
 2     // 定義成功的枚舉常量,狀態碼,和描述
 3     SUCCESS(0,"ok"),// 這里的代碼相當於:public static  final DataEnumCode SUCCESS = new DataEnumCode(0,“ok”)調用類有參構造傳值
 4     // 定義系統異常的枚舉常量,狀態碼,和描述
 5     SYSTEM_ERROR(5001,"服務器系統異常,請稍后..."),
 6     // 定義參數異常的枚舉常量,狀態碼,和描述
 7     PARAMETER_ERROR(5002,"參數異常,認證失敗..."),
 8     // 定義用戶名存在異常的枚舉常量,狀態碼,和描述
 9     USER_HAS_ERROR(5003,"用戶名已存在....");// 注意上面的是逗號分隔,這里結束是分號
10     
11     // 定義的枚舉常量屬性。
12     private int code;// 狀態碼
13     private String message;// 描述
14     
15     /**
16      * 私有構造,防止被外部調用
17      */
18     private EnumCode(int code, String message) {
19         this.code = code;
20         this.message = message;
21     }
22      /**
23      * 定義方法,返回描述,跟常規類的定義get沒區別
24      * @return
25      */
26     public int getCode() {
27         return code;
28     }
29     public String getMessage() {
30         return message;
31     }
32 }

2.定義Response的標准格式POJO

  為便於合理化實現標准格式的響應,新增POJO類,並添加封裝屬性(狀態碼,描述信息,響應數據)。

  為便於標准化的實施,類中提供的如下四個方法:(該解析響應類可以作為工具使用了,有心的朋友可自行保存一下)

  1. 成功方法。請求成功,響應結果集數據,響應狀態碼,描述,狀態碼和描述從枚舉常量中解析。
  2. 失敗方法。請求失敗,無結果集數據,響應狀態碼,描述保留,狀態碼和描述從枚舉常量中解析。(枚舉類型有限,不一定滿足所有異常)
  3. 失敗方法。請求失敗,無結果集數據,響應狀態碼,描述保留,該方法用於解決因為枚舉常量的局限性,不足以滿足所有需求的問題,實現允許自定義狀態碼和描述
  4. 提供便於解析枚舉常量的方法。
     1 @Data
     2 @NoArgsConstructor
     3 @AllArgsConstructor
     4 public class ResponseData<T>{
     5 
     6     private int code;// 狀態碼
     7     private String message;// 提示消息
     8     private T data;// 響應結果集數據
     9 
    10     /**枚舉類常量解析器
    11      * 快速解析枚舉類常量信息,解析數據並放入到標准響應類ResponseData的屬性中
    12      * @param enumCode
    13      */
    14     public void parserEnum(EnumCode enumCode) {
    15         this.code = enumCode.getCode();// 獲取枚舉常量的狀態碼,賦值給屬性
    16         this.message = enumCode.getMessage();// 獲取枚舉常量的描述信息
    17     }
    18 
    19     /**定義請求成功的:狀態碼,描述,結果集數據
    20      * @param data 傳遞的響應結果集數據
    21      * @return 有成功狀態碼,描述,結果集數據的標准格式對象
    22      */
    23     public static<T> ResponseData<T> success(T data) {
    24         // 創建響應標准格式對象
    25         ResponseData<T> responseData = new ResponseData<T>();
    26         // 調用轉換器方法,將(成功)枚舉常量解析,放入到標准響應數據中。
    27         responseData.parserEnum(EnumCode.SUCCESS);
    28         // 放入響應數據
    29         responseData.setData(data);
    30         return responseData;
    31     }
    32 
    33 
    34     /**定義請求失敗的:
    35      * @param enumCode 失敗時傳遞的常見錯誤枚舉常量
    36      * @return 有失敗狀態碼,描述,結果集可為null,也可為自定義異常信息
    37      */
    38     public static<T>  ResponseData<T> error(EnumCode enumCode,T data) {
    39         // 創建響應標准格式對象
    40         ResponseData<T> responseData = new ResponseData<T>();
    41         // 調用轉換器方法,將(錯誤)枚舉常量解析。
    42         responseData.parserEnum(enumCode);
    43         responseData.setData(data);
    44         return responseData;
    45     }
    46 
    47     /** 有成功,有失敗,但是失敗的狀態描述不一定能全部滿足需求(枚舉類有限),所以,自定義方法實現自定義信息
    48      * @param code 自定義的狀態碼
    49      * @param message  自定義的錯誤信息
    50      * @return  有失敗自定義狀態碼,自定義描述,結果集可為null,也可為自定義異常信息
    51      */
    52      public static<T> ResponseData<T> generator(int code,String message,T data) {
    53         // 創建響應標准格式對象
    54         ResponseData<T> responseData = new ResponseData<T>();
    55         responseData.setCode(code);
    56         responseData.setMessage(message);
    57         responseData.setData(data);
    58         return responseData;
    59     }
    60 
    61 }

溫馨提示:靜態方法定義泛型時,必須使用statc<T>定義,否則編譯失敗。

解惑:有人可能存在疑問,既然枚舉類不能滿足所有響應要求,干嘛定義枚舉類,感覺有點多此一舉!直接自定義封裝多好,可以解決所有問題。但是,請記住,團隊開發,如果全部使用自定義封裝,那么如何實現信息的統一標准呢?當出現同一個錯誤時,有人提示系統錯誤,有人提示后台錯誤,有人提示請聯系管理員???這樣是不是很亂。所以常見的,基本的消息定義,通過枚舉類列舉,可以輕松實現統一管理。而不常見的錯誤,既然不常見,那么又怎可能經常自定義?這就是簡易的架構設計優化。

3, 編寫Controller提供RESTful風格接口

 1 @Api(value = "測試SwaggerAPI Annotation", tags = "Swagger測試之用戶信息管理API")
 2 @RestController
 3 @RequestMapping("/user")
 4 public class SwaggerController {
 5     
 6     @ApiIgnore    // 忽略這個API
 7     @GetMapping("/hello")
 8     public String hello() {
 9         return "hello";
10     }
11     
12     @GetMapping(value = "/swaggerGet/{name}")
13     @ApiOperation(value = "接口方法說明", notes = "接口的詳情描述")
14     @ApiImplicitParam(name = "name", value = "請傳遞一個用戶名參數",required = false,dataType = "String", paramType = "path")
15     public ResponseData<String> swaggerGet(@PathVariable String name) {
16         // 調用成功的解析方法,並傳遞響應數據
17         ResponseData<String> responseData = ResponseData.success(name);
18         return responseData;
19     }
20     
21     @PostMapping(value = "/swaggerPost")
22     @ApiOperation(value = "新增用戶", notes = "Swagger測試RESTful之POST請求測試入參一個POJO(JSON格式)")
23     public ResponseData<User> swaggerGet(@RequestBody User user) {
24         // 調用成功的解析方法,並傳遞響應數據
25         ResponseData<User> responseData = ResponseData.success(user);
26         return responseData;
27     }
28     
29 }

4.打開瀏覽器測試訪問。

  瀏覽器測試訪問:http://localhost:8080/swagger-ui/index.html  (選擇測試一下,按照接口文檔說明實施測試)

  Postman測試訪問:(輸入接口URL,傳遞參數測試即可)

 

 測試結果如下:

 

 三,統一返回值高級處理方式

1.統一返回值標准格式存在的問題

  A,控制器方法返回值普遍需要變動類型為ResponseData,不靈活。

  B,方法返回數據前都必須有一段包裝代碼,最后返回ResponseData。(代碼啰嗦,不實用)

2.解決方案

  ResponseBodyAdvice接口,是SpringBoot提供的攔截Controller方法返回值的處理器增強類。

  ResponseBodyAdvice的主要作用:攔截Controller方法的返回值,統一處理返回值/響應體,一般用來做response的統一格式、加解密、簽名等

3.實現方案

  在上面代碼統一標准結果的基礎上實現:保留枚舉類,和統一結果集POJO類,而后繼續:

  1. 編寫普通類實現接口ResponseBodyAdvice
    接口中含有兩個方法:
    beforeBodyWrite:返回值增強處理方法。
    supports: 是否攔截控制器方法返回值,執行返回值增強。
  2. 修改方法supports返回值為true。
  3. 修改方法beforeBodyWrite,添加代碼如下
  4. 類上采用注解(切記不要省略屬性basePackages)
    @RestControllerAdvice(basePackages = "com.xsge123.app")
     1 // @ControllerAdvice:作用:對所有控制器中,被@RequestMapping注解標注的方法,進行增強(也可以直接使用@RestControllerAdvice)
     2 @RestControllerAdvice(basePackages  = "com.xsge123.app")   // 控制器類增強:可以對Controller中所有使用@RequestMapping注解的方法增強
     3 public class ResponseResult<T> implements ResponseBodyAdvice<Object>{
     4     
     5     /**被攔截的響應,立即執行該方法。
     6      * body :是請求控制器方法接口后,響應的內容。(其他參數不用了解)
     7      */
     8     @Override
     9     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
    10             Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
    11             ServerHttpResponse response) {
    12         
    13         // String類型不能直接包裝
    14         if (returnType.getGenericParameterType().equals(String.class)) {
    15             ObjectMapper objectMapper = new ObjectMapper();
    16                 // 將數據包裝在ResultVo里后轉換為json串進行返回
    17             try {
    18                 return objectMapper.writeValueAsString(ResponseData.success(body));
    19             } catch (JsonProcessingException e) {
    20                 // 這里正常應該加如自定義的統一異常處理
    21                 e.printStackTrace();
    22             }
    23         }
    24         -------------------------
    25         return ResponseData.success(body);
    26     }
    27     
    28     /**
    29      * 這個方法的返回值,決定是否啟動結果響應攔截,當返回為true是,表示攔截
    30      */
    31     @Override
    32     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    33         return true;
    34     }
    35 
    36 }

說明:

不要忘記添加注解:@RestControllerAdvice(basePackages = "com.xsge123.app")特別注意,這里不能省略basePackages屬性,否則不生效

@ControllerAdvice這是一個非常有用的注解,它的作用是增強Controller的擴展功能類(凡是控制器方法含有@RequestMapping注解的都會被執行增強指定功能)。

@ControllerAdvice:作用:對所有控制器中,被@RequestMapping注解標注的方法,進行增強(也可以直接使用@RestControllerAdvice)

該注解主要的場景用於:

1.增強異常處理的能力,實現異常統一處理。(該功能參數博主SpringBoot第七集統一異常處理)

2.增強返回數據處理能力,實現返回統一格式的JSON數據。(當前正是介紹演示的這個功能)

4.編寫控制器,提供暴露接口

  控制器方法返回值更靈活了...

 1 @Api(value = "測試SwaggerAPI Annotation", tags = "Swagger測試之人物信息管理API")
 2 @RestController
 3 @RequestMapping("/person")
 4 public class SwaggerControllerAdvice {
 5     
 6     
 7     @GetMapping(value = "/{name}")
 8     @ApiOperation(value = "Adivc接口方法說明", notes = "接口的詳情描述")
 9     @ApiImplicitParam(name = "name", value = "請傳遞一個用戶名參數",required = false,dataType = "String", paramType = "path")
10     public String swaggerGet(@PathVariable String name) {
11         return name;
12     }
13     
14     @PostMapping(value = "/swaggerPost")
15     @ApiOperation(value = "Adivc新增用戶", notes = "Swagger測試RESTful之POST請求測試入參一個POJO(JSON格式)")
16     public User swaggerGet(@RequestBody User user) {
17         return user;
18     }
19     
20 }

溫馨提示:控制注解使用的是@RestController。

5.訪問SwagerUI,實施測試。

  訪問地址:http://localhost:8080/swagger-ui/index.html

  測試結果我這里就暫時略過了,我行,不中用,得你行才是真的行!

四,再議整合統一異常

  在博主第七章中我們講解了SpringBoot統一異常處理,本章中又講解了統一數據返回值格式,這存在什么問題呢?

  問題:
  統一異常處理結果:(參考SpringBoot第七集:異常處理與整合JSR303校驗案例演示結果)

1 {
2     "timestamp": "2020-11-23T09:32:55.365+00:00",
3     "respCode": 0,
4     "respMsg": "服務器刷新異常,請稍后。。。",
5     "message": "/ by zero",
6     "exceptionName": "java.lang.ArithmeticException",
7     "path": "/queryPersonById/1",
8     "data": null
9 }

  統一響應返回值結果:(參考本章案例演示結果)

1 {
2     "code": 0,
3     "message": "ok",
4     "data": {
5         "username": "xsge",
6         "password": "2222"
7     }
8 }

  返回數據格式存存在兩種格式,既然統一,就應當全部統一(code,message,data)。因此需要深入處理,將異常處理也集成到統一響應格式中(統一格式的JSON)。

實現方式

  1.修改接口ResponseBodyAdvice實現類ResponseResult中方法beforeBodyWrite添加如下邏輯判斷:

 1 /**被攔截的響應,立即執行該方法。
 2      * body :是請求控制器方法接口后,響應的內容。(其他參數不用了解)
 3      */
 4     @Override
 5     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
 6             Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
 7             ServerHttpResponse response) {
 8         System.out.println(body);
 9         // String類型不能直接包裝
10         if (returnType.getGenericParameterType().equals(String.class)) {
11             ObjectMapper objectMapper = new ObjectMapper();
12                 // 將數據包裝在ResultVo里后轉換為json串進行返回
13             try {
14                 return objectMapper.writeValueAsString(ResponseData.success(body));
15             } catch (JsonProcessingException e) {
16                 // 這里正常應該加如自定義的統一異常處理
17                 e.printStackTrace();
18             }
19         // =============================================================
20         } else if(body instanceof ExceptionResponseResult) {// 判斷返回值結果是否是一個異常對象類型
21             // 如果是異常類型,傳入異常狀態碼(枚舉類型)和異常數據。
22             return ResponseData.error(EnumCode.SYSTEM_ERROR,body);
23         }
24         // =============================================================
25         return ResponseData.success(body);
26     }

  2.測試
  訪問接口:http://localhost:8080/person/swaggerPost
  入參:{"username": "xsge","password": "2222"}

1 @PostMapping(value = "/swaggerPost")
2     @ApiOperation(value = "Adivc新增用戶", notes = "Swagger測試RESTful之POST請求測試入參一個POJO(JSON格式)")
3     public User swaggerGet(@RequestBody User user) {
4         int a = 10/0;
5         return user;
6     }

  結果:

 1 {
 2     "code": 5001,
 3     "message": "服務器系統異常,請稍后...",
 4     "data": {
 5         "timestamp": "2020-11-23T12:05:55.794+00:00",
 6         "respMsg": "服務器刷新異常,請稍后。。。",
 7         "message": "/ by zero",
 8         "exceptionName": "java.lang.ArithmeticException",
 9         "path": "/person/swaggerPost"
10     }
11 }

  如果在更改時,覺得消息過於復雜,有些又覺得沒有用,可以自己定義修改類ExceptionResponseResult的屬性即可。一句話根據自己需求定義!

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM