我們在用Spring Boot去向前端提供Restful Api接口時,經常會遇到接口處理異常的情況,產生異常的可能原因是參數錯誤,空指針異常,SQL執行錯誤等等。
當發生這些異常時,Spring Boot會自動對異常進行一次統一的處理,返回一個異常信息:
@RestController
public class LiveRestController {
// 用戶查詢某一種直播狀態下的所有直播信息
@GetMapping("/lives")
public List<LiveResponse> getLiveList(@RequestParam Long status) {
// 這里特意寫上.toString()是為了觸發NPE
log.info("get status:{} lives", status.toString());
return Lists.newArrayList();
}
}
當不帶參數向這個接口發起請求時就會得到下面的結果:

這樣的結果對於用戶來說是很不友好的,前端也並不能夠向用戶展示這樣的錯誤信息。
我們可以看到這個異常的處理日志如下:
2019-10-15 23:03:25.351 WARN 31571 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required Long parameter 'status' is not present]
這個就是由Spring Boot內部異常處理類來處理的結果。對此,我們需要去實現定制自己的異常處理類,已實現更好的異常提示。
首先要創建全局異常處理類,就需要用到兩個重要的注解:
@ControllerAdvice定義統一的異常處理類,這樣就不必在每個Controller中逐個定義AOP去攔截處理異常。
@ExceptionHandler用來定義函數針對的異常類型,最后將Exception對象處理成自己想要的結果。
下面給出自定義異常處理的示例代碼:
// 注解定義異常統一處理類
@ControllerAdvice
public class RestfulApiExceptionHandler {
// 這里處理MissingServletRequestParameterException的異常
@ExceptionHandler(value = MissingServletRequestParameterException.class)
// 返回JSON數據
@ResponseBody
// 特別注意第二個參數為要處理的異常
public Map<String, Object> requestExceptionHandler(HttpServletRequest request, MissingServletRequestParameterException exception) {
Map<String, Object> error = Maps.newHashMap();
error.put("status", 500);
// 在這里可以定義返回的異常提示信息
error.put("message", "參數" + exception.getParameterName() + "錯誤");
return error;
}
// 這里處理沒有被上一個方法處理的異常,通常在最后的方法處理最大的Exception,保證兜底
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Map<String, Object> exceptionHandler(HttpServletRequest request, Exception exception) throws Exception {
Map<String, Object> error = Maps.newHashMap();
error.put("status", 500);
error.put("message", "系統錯誤");
return error;
}
}
如此異常信息會被處理成:

可以看到異常信息被改成了我們自定義的形式,但是這個時候的HTTP狀態碼被處理成了200,我們可以通過定義的status來完成錯誤請求的識別與處理。
