關(guān)于springboot的接口返回值統(tǒng)一標(biāo)準(zhǔn)格式
response統(tǒng)一格式
一、目標(biāo)
- 弄清楚為什么要對(duì)springboot,所有Controller的response做統(tǒng)一格式封裝?
- 學(xué)會(huì)用ResponseBodyAdvice接口 和 @ControllerAdvice注解
二、為什么要對(duì)springboot的接口返回值統(tǒng)一標(biāo)準(zhǔn)格式?
我們先來看下,springboot默認(rèn)情況下的response是什么格式的
第一種格式:response為String
@GetMapping(value="/getStr")
public String ?getStr( ?){
? ? return ?"test";
}以上springboot的返回值為
test
第二種格式:response為Objct
@GetMapping(value="/getObject")
public UserVO ?getObject( ?){
? ? UserVO vo=new UserVO();
? ? vo.setUsername("agan");
? ? return ?vo;
}以上springboot的返回值為
{
? "id": null,
? "username": "agan",
? "password": null,
? "email": null,
? "phone": null,
? "idCard": null,
? "sex": null,
? "deleted": null,
? "updateTime": null,
? "createTime": null
}第三種格式:response為void
@GetMapping(value="/empty")
public void ?empty( ?){
}以上springboot的返回值為空
第四種格式:response為異常
@GetMapping(value="/error")
public void ?error( ?){
? ? int i=9/0;
}以上springboot的返回值為空
{
? "timestamp": "2019-09-07T10:35:56.658+0000",
? "status": 500,
? "error": "Internal Server Error",
? "message": "/ by zero",
? "path": "/user/error"
}?以上4種,情況,如果你和客戶端(app h5)開發(fā)人聯(lián)調(diào)接口,他們會(huì)很懵逼,因?yàn)槟憬o他們的接口沒有一個(gè)統(tǒng)一的格式,客戶端開發(fā)人員,不知道如何處理返回值。
故,我們應(yīng)該統(tǒng)一response的標(biāo)準(zhǔn)格式。
三、定義response的標(biāo)準(zhǔn)格式
一般的response的標(biāo)準(zhǔn)格式包含3部分:
1.status狀態(tài)值:代表本次請(qǐng)求response的狀態(tài)結(jié)果。
2.response描述:對(duì)本次狀態(tài)碼的描述。
3.data數(shù)據(jù):本次返回的數(shù)據(jù)。
{
? ?"status":0,
? ?"desc":"成功",
? ?"data":"test"
}四、初級(jí)程序員對(duì)response代碼封裝
對(duì)response的統(tǒng)一封裝,是有一定的技術(shù)含量的,我們先來看下,初級(jí)程序員的封裝,網(wǎng)上很多教程都是這么寫的。
步驟1:把標(biāo)準(zhǔn)格式轉(zhuǎn)換為代碼
{
? ?"status":0,
? ?"desc":"成功",
? ?"data":"test"
}把以上格式轉(zhuǎn)換為Result代碼
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Result<T> {
? ? /**
? ? ?* 1.status狀態(tài)值:代表本次請(qǐng)求response的狀態(tài)結(jié)果。
? ? ?*/
? ? private Integer status;
? ? /**
? ? ?* 2.response描述:對(duì)本次狀態(tài)碼的描述。
? ? ?*/
? ? private String desc;
? ? /**
? ? ?* 3.data數(shù)據(jù):本次返回的數(shù)據(jù)。
? ? ?*/
? ? private T data;
? ? /**
? ? ?* 成功,創(chuàng)建ResResult:沒data數(shù)據(jù)
? ? ?*/
? ? public static Result suc() {
? ? ? ? Result result = new Result();
? ? ? ? result.setResultCode(ResultCode.SUCCESS);
? ? ? ? return result;
? ? }
? ? /**
? ? ?* 成功,創(chuàng)建ResResult:有data數(shù)據(jù)
? ? ?*/
? ? public static Result suc(Object data) {
? ? ? ? Result result = new Result();
? ? ? ? result.setResultCode(ResultCode.SUCCESS);
? ? ? ? result.setData(data);
? ? ? ? return result;
? ? }
? ? /**
? ? ?* 失敗,指定status、desc
? ? ?*/
? ? public static Result fail(Integer status, String desc) {
? ? ? ? Result result = new Result();
? ? ? ? result.setStatus(status);
? ? ? ? result.setDesc(desc);
? ? ? ? return result;
? ? }
? ? /**
? ? ?* 失敗,指定ResultCode枚舉
? ? ?*/
? ? public static Result fail(ResultCode resultCode) {
? ? ? ? Result result = new Result();
? ? ? ? result.setResultCode(resultCode);
? ? ? ? return result;
? ? }
? ? /**
? ? ?* 把ResultCode枚舉轉(zhuǎn)換為ResResult
? ? ?*/
? ? private void setResultCode(ResultCode code) {
? ? ? ? this.status = code.code();
? ? ? ? this.desc = code.message();
? ? }
}步驟2:把狀態(tài)碼存在枚舉類里面
public enum ResultCode ?{
?? ?/* 成功狀態(tài)碼 */
?? ?SUCCESS(0, "成功"),
?? ?/* 系統(tǒng)500錯(cuò)誤*/
?? ?SYSTEM_ERROR(10000, "系統(tǒng)異常,請(qǐng)稍后重試"),
?? ?UNAUTHORIZED(10401, "簽名驗(yàn)證失敗"),
?? ?/* 參數(shù)錯(cuò)誤:10001-19999 */
?? ?PARAM_IS_INVALID(10001, "參數(shù)無效"),
?? ?/* 用戶錯(cuò)誤:20001-29999*/
?? ?USER_HAS_EXISTED(20001, "用戶名已存在"),
?? ?USER_NOT_FIND(20002, "用戶名不存在");
?? ?private Integer code;
?? ?private String message;
?? ?ResultCode(Integer code, String message) {
?? ??? ?this.code = code;
?? ??? ?this.message = message;
?? ?}
?? ?public Integer code() {
?? ??? ?return this.code;
?? ?}
?? ?public String message() {
?? ??? ?return this.message;
?? ?}
}步驟3:加一個(gè)體驗(yàn)類
@Api(description = "用戶接口")
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
? ? @GetMapping(value="/getResult")
? ? public Result getResult( ?){
? ? ? ? return Result.suc("test");
? ? }
}結(jié)論:看到這里,應(yīng)該有很多同學(xué)都知道這樣封裝代碼有很大弊端。
因?yàn)榻窈竽忝繉懸粋€(gè)接口,都要手工指定Result.suc()這行代碼,多累????
如果你寫這種代碼推廣給你整個(gè)公司用,然后硬性規(guī)定代碼必須這么寫!!所有程序都會(huì)吐槽鄙視?。。?!
五、高級(jí)程序員對(duì)response代碼封裝
如果你在公司推廣你的編碼規(guī)范,為了避免被公司其他程序員吐槽和鄙視,我們必須優(yōu)化代碼。
優(yōu)化的目標(biāo):不要每個(gè)接口都手工指定Result返回值。
步驟1:采用ResponseBodyAdvice技術(shù)來實(shí)現(xiàn)response的統(tǒng)一格式
springboot提供了ResponseBodyAdvice來幫我們處理
ResponseBodyAdvice的作用:攔截Controller方法的返回值,統(tǒng)一處理返回值/響應(yīng)體,一般用來做response的統(tǒng)一格式、加解密、簽名等等。
先看下ResponseBodyAdvice這個(gè)接口的源碼。
public interface ResponseBodyAdvice<T> {
? ? /**
? ? ?* 是否支持advice功能
? ? ?* treu=支持,false=不支持
? ? ?*/
? ? boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
? ? /**
? ? ?*
? ? ?* 處理response的具體業(yè)務(wù)方法
? ? ?*/
? ? @Nullable
? ? T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}步驟2:寫一個(gè)ResponseBodyAdvice實(shí)現(xiàn)類
@ControllerAdvice(basePackages = "com.agan.boot")
public class ResponseHandler implements ResponseBodyAdvice<Object> {
? ? /**
? ? ?* 是否支持advice功能
? ? ?* treu=支持,false=不支持
? ? ?*/
? ? @Override
? ? public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
? ? ? ? return true;
? ? }
? ? /**
? ? ?*
? ? ?* 處理response的具體業(yè)務(wù)方法
? ? ?*/
? ? @Override
? ? public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
? ? ? ? if (o instanceof String) {
? ? ? ? ? ? return JsonUtil.object2Json(Result.suc(o));
? ? ? ? }
? ? ? ? return Result.suc(o);
? ? }
}以上代碼,有2個(gè)地方需要重點(diǎn)講解:
第1個(gè)地方:@ControllerAdvice 注解
@ControllerAdvice這是一個(gè)非常有用的注解,它的作用是增強(qiáng)Controller的擴(kuò)展功能類。
那@ControllerAdvice對(duì)Controller增強(qiáng)了哪些擴(kuò)展功能呢?主要體現(xiàn)在2方面:
- 對(duì)Controller全局?jǐn)?shù)據(jù)統(tǒng)一處理,例如,我們這節(jié)課就是對(duì)response統(tǒng)一封裝。
- 對(duì)Controller全局異常統(tǒng)一處理,這個(gè)后面的課程會(huì)詳細(xì)講解。
在使用@ControllerAdvice時(shí),還要特別注意,加上basePackages,
@ControllerAdvice(basePackages = “com.agan.boot”)
因?yàn)槿绻患拥脑挘墒菍?duì)整個(gè)系統(tǒng)的Controller做了擴(kuò)展功能,
它會(huì)對(duì)某些特殊功能產(chǎn)生沖突,例如 不加的話,在使用swagger時(shí)會(huì)出現(xiàn)空白頁(yè)異常。
第2個(gè)地方:beforeBodyWrite方法體的response類型判斷
if (o instanceof String) {
? ? ? ? ? ? return JsonUtil.object2Json(ResResult.suc(o));
}以上代碼一定要加,因?yàn)镃ontroller的返回值為String的時(shí)候,它是直接返回String,不是json,
故我們要手工做下json轉(zhuǎn)換處理
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot動(dòng)態(tài)加載Echarts柱狀圖
這篇文章主要為大家詳細(xì)介紹了springboot動(dòng)態(tài)加載Echarts柱狀圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
Java棧之鏈?zhǔn)綏4鎯?chǔ)結(jié)構(gòu)的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java棧之鏈?zhǔn)綏4鎯?chǔ)結(jié)構(gòu)的實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04
Jeecg-Boot異常處理'jeecg-boot.QRTZ_LOCKS'?doesn'
這篇文章主要介紹了Jeecg-Boot異常處理'jeecg-boot.QRTZ_LOCKS'?doesn't?exist問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
關(guān)于Spring源碼深度解析(AOP功能源碼解析)
這篇文章主要介紹了關(guān)于Spring源碼深度解析(AOP功能源碼解析),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
解析SpringBoot整合SpringDataRedis的過程
這篇文章主要介紹了SpringBoot整合SpringDataRedis的過程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06

