預設情況下,SpringBoot會有以下三種回傳情況。
@GetMapping("/getUserName") public String getUserName(){ return "HuaGe"; }
呼叫介面傳回結果:
HuaGe
@GetMapping("/getUserName") public User getUserName(){ return new User("HuaGe",18,"男"); }
呼叫介面傳回結果:
{ "name": "HuaGe", "age": "18", "性别": "男", }
@GetMapping("/getUserName") public static String getUserName(){ HashMap hashMap = Maps.newHashMap(); return hashMap.get(0).toString(); }
模擬一個空指標異常,在不做任何異常處理的情況下,可以看下SpringBoot的預設回傳結果:
{ "timestamp": "2021-08-09T06:56:41.524+00:00", "status": 500, "error": "Internal Server Error", "path": "/sysUser/getUserName" }
對於上面這幾種情況,如果整個專案沒有定義統一的返回格式,五個後台開發人員定義五種返回格式,這樣不僅代碼臃腫,前後端對接效率低,而且還會有一些意向不到的情況發生,例如前端直接顯示異常詳情等,這給用戶的體驗是非常差的。
專案中最常見到的是封裝一個工具類,類別中定義需要返回的字段信息,把需要返回前端的接口信息,通過該類進行封裝,這樣就可以解決返回格式不統一的現象了。
code: 狀態碼,後台可以維護一套統一的狀態碼;
##message: 描述訊息,介面呼叫成功/失敗的提示訊息;
data: 傳回資料。
#新建Result類別
##public class Result<T> { private int code; private String message; private T data; public Result() {} public Result(int code, String message) { this.code = code; this.message = message; } /** * 成功 */ public static <T> Result<T> success(T data) { Result<T> result = new Result<T>(); result.setCode(ResultMsgEnum.SUCCESS.getCode()); result.setMessage(ResultMsgEnum.SUCCESS.getMessage()); result.setData(data); return result; } /** * 失败 */ public static <T> Result<T> error(int code, String message) { return new Result(code, message); } }
定義回傳狀態碼
public enum ResultMsgEnum { SUCCESS(0, "成功"), FAIL(-1, "失败"), AUTH_ERROR(502, "授权失败!"), SERVER_BUSY(503, "服务器正忙,请稍后再试!"), DATABASE_OPERATION_FAILED(504, "数据库操作失败"); private int code; private String message; ResultMsgEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return this.code; } public String getMessage() { return this.message; } }
使用方式
狀態碼
,接下來就要看下在介面中如何使用了。@GetMapping("/getUserName") public Result getUserName(){ return Result.success("huage"); }
{ "code": 0, "message": "成功", "data": "huage" }
Result.success來包裝回訊息,會新增很多重複程式碼,顯得不夠優雅,甚至都不好意思拿出去顯擺。肯定會有一種方式能夠再一次提高程式碼逼格,實現最優解。 三、進階用法
@RestControllerAdvice:
3.2 用法說明
新
ResponseAdvice
supports、
beforeBodyWrite方法;
該類別用於統一封裝controller中介面的傳回結果。
@RestControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice<Object> { @Autowired private ObjectMapper objectMapper; /** * 是否开启功能 true:是 */ @Override public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { return true; } /** * 处理返回结果 */ @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { //处理字符串类型数据 if(o instanceof String){ try { return objectMapper.writeValueAsString(Result.success(o)); } catch (JsonProcessingException e) { e.printStackTrace(); } } return Result.success(o); } }我們可以透過
getUserName
介面測試一下,會發現和直接使用ResponseAdvice我們全部使用了
Result.success(o)
{ "code": 0, "message": "成功", "data": { "timestamp": "2021-08-09T09:33:26.805+00:00", "status": 405, "error": "Method Not Allowed", "path": "/sysUser/getUserName" } }
雖然格式上沒有毛病,但在code、data欄位的特定資料上是不友善或不正確的。不處理好這些事情,會嚴重影響自己在前端妹妹心中的高大形象的,這是絕對不能容忍的。
3.3 全域例外處理器以前我們遇到例外時,第一時間想到的應該是try..catch..finnal吧,不過這種方式會導致大量程式碼重複,維護困難,邏輯臃腫等問題,這不是我們想要的結果。
今天我們要用的全域異常處理方式,用起來是比較簡單的。首先新增一個類,增加
@RestControllerAdvice
@RestControllerAdvice public class CustomerExceptionHandler { }###如果我們有想要攔截的例外類型,就新增一個方法,使用###@ExceptionHandler###註解修飾,註解參數為目標例外類型。 ######例如:controller中介面發生Exception異常時,就會進入到###Execption###方法中進行捕獲,將雜亂的異常訊息,轉換成指定格式後交給###ResponseAdvice# ##方法進行統一格式封裝並回傳給前端小夥伴。 ###
@RestControllerAdvice @Slf4j public class CustomerExceptionHandler { @ExceptionHandler(AuthException.class) public String ErrorHandler(AuthorizationException e) { log.error("没有通过权限验证!", e); return "没有通过权限验证!"; } @ExceptionHandler(Exception.class) public Result Execption(Exception e) { log.error("未知异常!", e); return Result.error(ResultMsgEnum.SERVER_BUSY.getCode(),ResultMsgEnum.SERVER_BUSY.getMessage()); } }
再次调用接口getUserName
查看返回结果,会发现还是有一些问题,因为我们在CustomerExceptionHandler
中已经将接口返回结果封装成Result
类型,而代码执行到统一结果返回类ResponseAdvice
时,又会结果再次封装,就出现了如下问题。
{ "code": 0, "message": "成功", "data": { "code": 503, "message": "服务器正忙,请稍后再试!", "data": null } }
解决上述问题非常简单,只要在beforeBodyWrite
中增加一条判断即可。
@RestControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice<Object> { @Autowired private ObjectMapper objectMapper; /** * 是否开启功能 true:开启 */ @Override public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { return true; } /** * 处理返回结果 */ @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { //处理字符串类型数据 if(o instanceof String){ try { return objectMapper.writeValueAsString(Result.success(o)); } catch (JsonProcessingException e) { e.printStackTrace(); } } //返回类型是否已经封装 if(o instanceof Result){ return o; } return Result.success(o); } }
以上是SpringBoot統一介面回傳及全域異常如何處理的詳細內容。更多資訊請關注PHP中文網其他相關文章!