springboot 後端統一返回資料格式,異常統一處理
阿新 • • 發佈:2022-05-12
springboot 後端統一返回資料格式,異常統一處理
模板
場景:
- 後端給前端的資料型別可能會是基本資料型別、String字串、物件、陣列、或者異常提示等。前端拿到你返回的資料去展示或者給出錯誤提示,但他不可能說每個介面都把這些異常提示處理一遍,比如說返回沒有登入、或者一些業務異常等。
分析:
- 基於上面場景,那麼我們要做的就是在後端返回結果前做一層統一處理。返回一個統一的物件,如ResponseVO,有code、msg、data;前端根據返回的code做統一處理
- code=0,返回成功,返回資料在data上
- code=1或其他,後端異常返回,可能是業務異常,也可能是程式異常,錯誤資訊放在msg上
- 如果未登入,後端返回403,這時候前端在呼叫後端介面返回那裡根據錯誤碼去做統一的處理,統一提示或其他。成功的話就把返回的資料data給對應呼叫方法那裡。
實現:
初級版,我們返回一個map,然後通過map把code、msg、data 放進去
@RequestMapping("/test") public Map<String,Object> test(){ Map<String,Object> map = new HashMap<>(); map.put("code","0"); map.put("msg","成功"); map.put("data","測試"); return map; }
- 返回結果:
- 上面圖片我們可以看到,滿足了我們的需求,返回了code、msg、還有我們的資料data。
- 但是問題來了,我們每個方法都要寫一遍map,把這些資料放進去是不是很麻煩呢,在上面花這麼多時間去寫這個還怎麼摸魚呢,因此我們小小的優化一下就有了我們的進階版
進階版,統一封裝:定義一個統一的返回物件ResponseVO ,在ResponseVO 裡寫成功和失敗的方法
@Data public class ResponseVO implements Serializable { /** * 響應狀態碼,0-成功,非0-失敗 */ private Integer code = 0; /** * 返回結果說明 */ private String msg = "成功"; /** * JSON格式響應資料 */ private Object data; /** * 返回成功 * @param data * @return */ public static ResponseVO success(Object data){ ResponseVO response = new ResponseVO(); response.setCode(0); response.setMsg("成功"); response.setData(data); return response; } }
- 這時候在controller呼叫就變成了下面這樣,是不是簡潔多了呢
@RequestMapping("/test1")
public ResponseVO test1(){
return ResponseVO.success("測試1");
}
- 現在雖然簡潔多了,但是還是在每個方法上都要寫ResponseVO.success()或者ResponseVO.fail(),而且每個方法的返回值都變成了ResponseVO,我們都不知道他們的意義了,那有沒有統一處理的呢,就是我該返回啥就返回啥,controller層不用關心這些?答案當然是有的,因此就有了下面的最終版方案。
最終版,ResponseBodyAdvice
- 接下來就要用到ResponseBodyAdvice,從字面意思理解它的意思就是返回體切面,就是對Controller返回的資料進行統一處理,因此我們只要實現這個介面,在上面做統一處理即可,他有兩個介面,我們只需在beforeBodyWrite方法處理就可以了,唯一要注意的就是當返回String型別時要特殊處理,不然會報轉換錯誤。統一封裝後就不用去關心返回型別了。
@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {
private Log log = LogFactory.getLog(ResponseHandler.class);
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
ResponseVO respVo = null;
if (body instanceof ResponseVO) {
respVo = (ResponseVO) body;
}else {
respVo = new ResponseVO();
respVo.setData(body);
}
//如果返回的字串型別,會先判斷HttpMessageConverter能否支援對應的返回型別再使用ResponseBodyAdvice進行封裝
//那麼此時在進來就不是String型別,所以會報無法轉換成ResponseVO物件,那麼這裡有兩種方法,一種是直接返回json字串,另一種是
//一種是自己的WebConfig進行額外的配置
if (body instanceof String){
return JSONUtil.toJsonStr(respVo);
}
return respVo;
}
}
問題1、假如有個介面特殊,不需要這個返回這個格式怎麼辦呢?
- 我們可以用到ResponseBodyAdvice介面的另一個方法,讓你的方法返回值不走這個統一返回格式處理,最好的方式就是定一個註解,在需要忽略的方法上加上這個註解,實現方式如下
- 定義註解IgnoreResponseHandler
@Documented
@Inherited
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreResponseHandler {
}
- 在ResponseBodyAdvice的supports方法忽略
@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {
private Log log = LogFactory.getLog(ResponseHandler.class);
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return !returnType.hasMethodAnnotation(IgnoreResponseHandler.class);
}
}
- 使用,返回結果,這樣就忽略掉了
@RequestMapping("/test5")
@IgnoreResponseHandler
public String test5(){
return "測試1";
}
統一處理返回的業務異常
- 對於異常我們想統一處理,就要用到@ExceptionHandler(value = Exception.class)這個註解了,加上這個註解,當丟擲異常時都會進這個方法
@ExceptionHandler(value = Exception.class)
public ResponseVO onException(HttpServletRequest request, Exception ex) {
ResponseVO resp = null;
if (ex instanceof AppException) {
resp = new ResponseVO((AppException) ex);
} else {
AppException exception = new AppException(9999,"未知異常");
resp = new ResponseVO(exception);
log.error("未知異常:", ex);
}
return resp;
}
- 使用
@RequestMapping("/test3")
public String test3(){
throw new AppException("測試異常");
}