關(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)格式?
我們先來(lái)看下,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種,情況,如果你和客戶(hù)端(app h5)開(kāi)發(fā)人聯(lián)調(diào)接口,他們會(huì)很懵逼,因?yàn)槟憬o他們的接口沒(méi)有一個(gè)統(tǒng)一的格式,客戶(hù)端開(kāi)發(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ù)含量的,我們先來(lái)看下,初級(jí)程序員的封裝,網(wǎng)上很多教程都是這么寫(xiě)的。
步驟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:沒(méi)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)碼存在枚舉類(lè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ù)無(wú)效"),
?? ?/* 用戶(hù)錯(cuò)誤:20001-29999*/
?? ?USER_HAS_EXISTED(20001, "用戶(hù)名已存在"),
?? ?USER_NOT_FIND(20002, "用戶(hù)名不存在");
?? ?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)類(lèi)
@Api(description = "用戶(hù)接口")
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
? ? @GetMapping(value="/getResult")
? ? public Result getResult( ?){
? ? ? ? return Result.suc("test");
? ? }
}結(jié)論:看到這里,應(yīng)該有很多同學(xué)都知道這樣封裝代碼有很大弊端。
因?yàn)榻窈竽忝繉?xiě)一個(gè)接口,都要手工指定Result.suc()這行代碼,多累啊??
如果你寫(xiě)這種代碼推廣給你整個(gè)公司用,然后硬性規(guī)定代碼必須這么寫(xiě)?。∷谐绦蚨紩?huì)吐槽鄙視?。。。?/p>
五、高級(jí)程序員對(duì)response代碼封裝
如果你在公司推廣你的編碼規(guī)范,為了避免被公司其他程序員吐槽和鄙視,我們必須優(yōu)化代碼。
優(yōu)化的目標(biāo):不要每個(gè)接口都手工指定Result返回值。
步驟1:采用ResponseBodyAdvice技術(shù)來(lái)實(shí)現(xiàn)response的統(tǒng)一格式
springboot提供了ResponseBodyAdvice來(lái)幫我們處理
ResponseBodyAdvice的作用:攔截Controller方法的返回值,統(tǒng)一處理返回值/響應(yīng)體,一般用來(lái)做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:寫(xiě)一個(gè)ResponseBodyAdvice實(shí)現(xiàn)類(lèi)
@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ò)展功能類(lèi)。
那@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)槿绻患拥脑?huà),它可是對(duì)整個(gè)系統(tǒng)的Controller做了擴(kuò)展功能,
它會(huì)對(duì)某些特殊功能產(chǎn)生沖突,例如 不加的話(huà),在使用swagger時(shí)會(huì)出現(xiàn)空白頁(yè)異常。
第2個(gè)地方:beforeBodyWrite方法體的response類(lèi)型判斷
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-09
Spring Boot 使用WebAsyncTask異步返回結(jié)果
這篇文章主要介紹了Spring Boot 使用WebAsyncTask異步返回結(jié)果的相關(guān)資料,需要的朋友可以參考下2018-02-02
java實(shí)現(xiàn)的xml格式化實(shí)現(xiàn)代碼
這篇文章主要介紹了java實(shí)現(xiàn)的xml格式化實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-11-11
JDBC連接Mysql的5種方式實(shí)例總結(jié)
JDBC是Java DataBase Connectivity技術(shù)的簡(jiǎn)稱(chēng),是一種可用于執(zhí)行 SQL語(yǔ)句的Java API,下面這篇文章主要給大家介紹了關(guān)于JDBC連接Mysql的5種方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
通過(guò)JDK源碼學(xué)習(xí)InputStream詳解
InputStream抽象類(lèi)是所有字節(jié)輸入流的類(lèi)的超類(lèi)。這篇文章主要給大家介紹了關(guān)于通過(guò)JDK源碼學(xué)習(xí)InputStream的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11

