關(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)空白頁異常。
第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實(shí)現(xiàn)HTTP調(diào)用的七種方式總結(jié)
小編在工作中,遇到一些需要調(diào)用三方接口的任務(wù),就需要用到 HTTP 調(diào)用工具,這里,我總結(jié)了一下 實(shí)現(xiàn) HTTP 調(diào)用的方式,共有 7 種(后續(xù)會(huì)繼續(xù)新增),需要的朋友可以參考下2023-09-09Spring Boot 使用WebAsyncTask異步返回結(jié)果
這篇文章主要介紹了Spring Boot 使用WebAsyncTask異步返回結(jié)果的相關(guān)資料,需要的朋友可以參考下2018-02-02java實(shí)現(xiàn)的xml格式化實(shí)現(xiàn)代碼
這篇文章主要介紹了java實(shí)現(xiàn)的xml格式化實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-11-11JDBC連接Mysql的5種方式實(shí)例總結(jié)
JDBC是Java DataBase Connectivity技術(shù)的簡稱,是一種可用于執(zhí)行 SQL語句的Java API,下面這篇文章主要給大家介紹了關(guān)于JDBC連接Mysql的5種方式,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04通過JDK源碼學(xué)習(xí)InputStream詳解
InputStream抽象類是所有字節(jié)輸入流的類的超類。這篇文章主要給大家介紹了關(guān)于通過JDK源碼學(xué)習(xí)InputStream的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11