怎么查看網(wǎng)站的安全性百度推廣優(yōu)化怎么做
統(tǒng)一的標(biāo)準(zhǔn)數(shù)據(jù)格式好處
SpringBoot返回統(tǒng)一的標(biāo)準(zhǔn)數(shù)據(jù)格式主要有以下幾點好處:
-
增強(qiáng)接口的可讀性和可維護(hù)性,使得前端開發(fā)人員能夠更加清晰地理解接口返回的數(shù)據(jù)結(jié)構(gòu),從而提高開發(fā)效率。
-
降低前后端耦合度,當(dāng)后端需要修改返回數(shù)據(jù)結(jié)構(gòu)時,只需要調(diào)整統(tǒng)一的數(shù)據(jù)格式,而不用修改大量的前端代碼。
-
有利于統(tǒng)一異常處理,對于出錯時的返回數(shù)據(jù)格式也是一致的,便于前端統(tǒng)一處理錯誤信息。
-
當(dāng)接口版本的升級和兼容時,可以通過統(tǒng)一的數(shù)據(jù)格式進(jìn)行向下兼容或者向上兼容,減少接口變更帶來的影響
格式不同的痛楚
一般情況下,SpringBoot
服務(wù)的返回格式有如下幾種:
第一種:返回String
@GetMapping("/hello")
public?String?getStr()?{return?"hello";
}
此時調(diào)用接口獲取到的返回值是這樣:
hello
第二種:返回自定義對象
@GetMapping("/aniaml")
public?Aniaml?getAniaml(){Aniaml?aniaml?=?new?Aniaml(1,"pig");return?aniaml;
}
此時調(diào)用接口獲取到的返回值是這樣:
{"id":?1,"name":?"pig"
}
第三種:接口異常
@GetMapping("/error")
public?int?error(){int?i?=?10/0;return?i;
}
此時調(diào)用接口獲取到的返回值是這樣:
{"timestamp":?"2021-07-08T08:05:15.423+00:00","status":?500,"error":?"Internal?Server?Error","path":?"/error"
}
看了上邊的幾種情況,不難發(fā)現(xiàn),一個接口在不同情況下,返回的數(shù)據(jù)格式竟然截然不同?那作為API的使用方估計就要罵娘了。此時,返回統(tǒng)一的標(biāo)準(zhǔn)數(shù)據(jù)格式就顯得很有必要。
標(biāo)準(zhǔn)格式
一個標(biāo)準(zhǔn)的返回格式應(yīng)包含以下三部分:
-
Status(狀態(tài)):由后端統(tǒng)一定義各種返回結(jié)果的狀態(tài)碼。
-
Message(描述):描述本次接口調(diào)用的結(jié)果信息。
-
Data(數(shù)據(jù)):包含本次返回的具體數(shù)據(jù)內(nèi)容。
{"status":"100","message":"操作成功","data":"hello"
}
當(dāng)然了這并不是絕對的,在有了主要的字段后,可以按需加入其他擴(kuò)展值,比如我們就在返回對象中添加了接口調(diào)用時間timestamp
等。
定義返回對象
@Data
public?class?ResultData<T>?{private?int?status;private?String?message;private?T?data;private?long?timestamp?;public?ResultData?(){this.timestamp?=?System.currentTimeMillis();}public?static?<T>?ResultData<T>?success(T?data)?{ResultData<T>?resultData?=?new?ResultData<>();resultData.setStatus(ReturnCode.RC100.getCode());resultData.setMessage(ReturnCode.RC100.getMessage());resultData.setData(data);return?resultData;}public?static?<T>?ResultData<T>?fail(int?code,?String?message)?{ResultData<T>?resultData?=?new?ResultData<>();resultData.setStatus(code);resultData.setMessage(message);return?resultData;}}
定義狀態(tài)碼
public?enum?ReturnCode?{/**操作成功**/RC100(100,"操作成功"),/**操作失敗**/RC999(999,"操作失敗"),/**服務(wù)限流**/RC200(200,"服務(wù)開啟限流保護(hù),請稍后再試!"),/**服務(wù)降級**/RC201(201,"服務(wù)開啟降級保護(hù),請稍后再試!"),/**熱點參數(shù)限流**/RC202(202,"熱點參數(shù)限流,請稍后再試!"),/**系統(tǒng)規(guī)則不滿足**/RC203(203,"系統(tǒng)規(guī)則不滿足要求,請稍后再試!"),/**授權(quán)規(guī)則不通過**/RC204(204,"授權(quán)規(guī)則不通過,請稍后再試!"),/**access_denied**/RC403(403,"無訪問權(quán)限,請聯(lián)系管理員授予權(quán)限"),/**access_denied**/RC401(401,"匿名用戶訪問無權(quán)限資源時的異常"),/**服務(wù)異常**/RC500(500,"系統(tǒng)異常,請稍后重試"),CLIENT_AUTHENTICATION_FAILED(1001,"客戶端認(rèn)證失敗"),USERNAME_OR_PASSWORD_ERROR(1002,"用戶名或密碼錯誤"),UNSUPPORTED_GRANT_TYPE(1003,?"不支持的認(rèn)證模式");/**自定義狀態(tài)碼**/private?final?int?code;/**自定義描述**/private?final?String?message;ReturnCode(int?code,?String?message){this.code?=?code;this.message?=?message;}public?int?getCode()?{return?code;}public?String?getMessage()?{return?message;}
}
統(tǒng)一返回格式
經(jīng)過上邊我們定義的數(shù)據(jù)格式,再次調(diào)用API,在Controller
層通過ResultData.success()
對返回結(jié)果進(jìn)行包裝后返回給前端。
@GetMapping("/hello")
public?String?getStr()?{return?ResultData.success("hello");
}
此時調(diào)用接口獲取到的返回值已經(jīng)是標(biāo)準(zhǔn)的格式,符合我們預(yù)期的效果。
{"status":?100,"message":?"hello","data":?null,"timestamp":?1625736481648
}
最大的弊端就是我們后面每寫一個接口都需要調(diào)用ResultData.success()
這行代碼對結(jié)果進(jìn)行包裝,重復(fù)勞動,浪費(fèi)體力;而且還很容易被其他老鳥給嘲笑。所以呢我們需要對代碼進(jìn)行優(yōu)化,目標(biāo)就是不要每個接口都手工制定ResultData
返回值。
高級實現(xiàn)方式
要優(yōu)化這段代碼很簡單,我們只需要借助SpringBoot提供的ResponseBodyAdvice
即可。
“
ResponseBodyAdvice的作用:攔截Controller方法的返回值,統(tǒng)一處理返回值/響應(yīng)體,一般用來統(tǒng)一返回格式,加解密,簽名等等。
”
先來看下ResponseBodyAdvice
的源碼:
public?interface?ResponseBodyAdvice<T>?{/***?是否支持advice功能*?true?支持,false?不支持*/boolean?supports(MethodParameter?var1,?Class<??extends?HttpMessageConverter<?>>?var2);/***?對返回的數(shù)據(jù)進(jìn)行處理*/@NullableT?beforeBodyWrite(@Nullable?T?var1,?MethodParameter?var2,?MediaType?var3,?Class<??extends?HttpMessageConverter<?>>?var4,?ServerHttpRequest?var5,?ServerHttpResponse?var6);
}
我們只需要編寫一個具體實現(xiàn)類即可
/***?@author?jam*?@date?2021/7/8?10:10?上午*/
@RestControllerAdvice
public?class?ResponseAdvice?implements?ResponseBodyAdvice<Object>?{@Autowiredprivate?ObjectMapper?objectMapper;@Overridepublic?boolean?supports(MethodParameter?methodParameter,?Class<??extends?HttpMessageConverter<?>>?aClass)?{return?true;}@SneakyThrows@Overridepublic?Object?beforeBodyWrite(Object?o,?MethodParameter?methodParameter,?MediaType?mediaType,?Class<??extends?HttpMessageConverter<?>>?aClass,?ServerHttpRequest?serverHttpRequest,?ServerHttpResponse?serverHttpResponse)?{if(o?instanceof?String){return?objectMapper.writeValueAsString(ResultData.success(o));}????????return?ResultData.success(o);}
}
需要注意兩個地方:@RestControllerAdvice
注解
@RestControllerAdvice
是@RestController
注解的增強(qiáng),可以實現(xiàn)三個方面的功能:
-
全局異常處理
-
全局?jǐn)?shù)據(jù)綁定
-
全局?jǐn)?shù)據(jù)預(yù)處理
- String類型判斷
if(o?instanceof?String){return?objectMapper.writeValueAsString(ResultData.success(o));
}?
這段代碼一定要加,如果Controller直接返回String的話,SpringBoot是直接返回,故我們需要手動轉(zhuǎn)換成json。
經(jīng)過上面的處理我們就再也不需要通過ResultData.success()
來進(jìn)行轉(zhuǎn)換了,直接返回原始數(shù)據(jù)格式,SpringBoot自動幫我們實現(xiàn)包裝類的封裝。
@GetMapping("/hello")
public?String?getStr(){return?"hello";
}
此時我們調(diào)用接口返回的數(shù)據(jù)結(jié)果為:
@GetMapping("/hello")
public?String?getStr(){return?"hello";
}
是不是感覺很完美,別急,還有個問題在等著你呢。
接口異常問題
此時有個問題,由于我們沒對Controller的異常進(jìn)行處理,當(dāng)我們調(diào)用的方法一旦出現(xiàn)異常,就會出現(xiàn)問題,比如下面這個接口:
@GetMapping("/wrong")
public?int?error(){int?i?=?9/0;return?i;
}