1、為什么要全局異常處理
在實際開發中,如果不處理統一處理異常,那么前端在調用后端提供的接口,就會處理各種的異常結構,對於前端來說那可謂是一場災難,這對前后端的協作也不友好。比如后端路徑:/api/v1/index/user?id=222
,如果前端未傳入ID,那么SpringBoot就會報如下異常:
{"timestamp":"2021-03-30T15:22:46.139+00:00","status":400,"error":"Bad Request","message":"","path":"/api/v1/index/user"}
或者是我們代碼里面自定義異常throw new RuntimeException("用戶信息未找到!");
,對於Spring Boot來說拋出如下異常:
{"timestamp":"2021-03-30T15:25:55.657+00:00","status":500,"error":"Internal Server Error","message":"","path":"/api/v1/index/users"}
而后端真的異常信息是用戶信息未找到!
,前端獲取到500異常,完全不知后端的具體異常。基於這些就有必要全局統一處理異常。
2、實現全局異常處理
2.1 封裝統一結構消息
在實現全局異常處理之前,我們先封裝一個統一結構的消息對象。
- 消息類
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class RestResponse {
private String code;
private String message;
private Object data;
}
- 異常狀態枚舉
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ExceptionStatusEnums {
OK("0","success"),
ERROR("1","error");
private String code;
private String msg;
}
- 處理消息的工廠類
import com.tenghu.lf.enums.ExceptionStatusEnums;
import com.tenghu.lf.resp.RestResponse;
public class ResponseFactory {
private ResponseFactory(){
}
public static RestResponse getOkResponse(){
return new RestResponse(ExceptionStatusEnums.OK.getCode(),ExceptionStatusEnums.OK.getMsg(),null);
}
public static RestResponse getOkResponse(String message){
return new RestResponse(ExceptionStatusEnums.OK.getCode(),message,null);
}
public static RestResponse getOkResponse(Object data){
return new RestResponse(ExceptionStatusEnums.OK.getCode(),ExceptionStatusEnums.OK.getMsg(),data);
}
public static RestResponse getOkResponse(String message,Object data){
return new RestResponse(ExceptionStatusEnums.OK.getCode(),message,data);
}
public static RestResponse getErrorMessage(){
return new RestResponse(ExceptionStatusEnums.ERROR.getCode(),ExceptionStatusEnums.ERROR.getMsg(),null);
}
public static RestResponse getErrorMessage(String message){
return new RestResponse(ExceptionStatusEnums.ERROR.getCode(),message,null);
}
public static RestResponse getErrorMessage(String message,Object data){
return new RestResponse(ExceptionStatusEnums.ERROR.getCode(),message,data);
}
public static RestResponse getBuildMessage(String code,String message){
return new RestResponse(code,message,null);
}
public static RestResponse getBuildMessage(String code,String message,Object data){
return new RestResponse(code,message,data);
}
}
2.2 實現全局異常
定義一個全局異常處理類,在類上加上@RestControllerAdvice
注解,也可以使用@ControllerAdvice
注解,只不過使用后者,需要在各個方法上加上@ResponseBody
注解
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 處理自定義異常
* @param componentException
* @return
*/
@ExceptionHandler(ComponentException.class)
public RestResponse componentException(ComponentException componentException) {
componentException.printStackTrace();
return ResponseFactory.getBuildMessage(componentException.getCode(),componentException.getLocalizedMessage());
}
/**
* 處理請求參數異常
* @param e
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public RestResponse paramMissingException(MissingServletRequestParameterException e){
e.printStackTrace();
return ResponseFactory.getBuildMessage(HttpStatus.INTERNAL_SERVER_ERROR.value()+"",String.format("請求參數:%s不能為空!",e.getParameterName()));
}
/**
* 處理請求體異常
* @param e
* @return
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public RestResponse readableException(HttpMessageNotReadableException e){
e.printStackTrace();
return ResponseFactory.getBuildMessage(HttpStatus.INTERNAL_SERVER_ERROR.value()+"",String.format("請求參數體不能為空!%s",e.getLocalizedMessage()));
}
/**
* 處理其他未知的運行時異常
* @param runtimeException
* @return
*/
@ExceptionHandler(RuntimeException.class)
public RestResponse runtimeException(RuntimeException runtimeException) {
runtimeException.printStackTrace();
return ResponseFactory.getBuildMessage(HttpStatus.INTERNAL_SERVER_ERROR.value()+"",runtimeException.getLocalizedMessage());
}
/**
* 處理其他異常
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
public RestResponse otherException(Exception exception){
exception.printStackTrace();
return ResponseFactory.getErrorMessage(exception.getLocalizedMessage());
}
}
3、測試
- 訪問路徑
/api/v1/index/user?name=小A
,不傳name
的情況下。
{
"code": "500",
"message": "請求參數:name不能為空!",
"data": null
}
- 訪問路徑
/api/v1/index/user?name=小A
,傳name
的情況下。
{
"code": "0",
"message": "success",
"data": {
"id": "1111",
"name": "小A"
}
}
- 訪問路徑
/api/v1/index
,不傳請求體。
{
"code": "500",
"message": "請求參數體不能為空!Required request body is missing: public com.tenghu.lf.resp.RestResponse com.tenghu.lf.controller.IndexController.save(com.tenghu.lf.entity.User)",
"data": null
}
- 訪問路徑
/api/v1/index
,傳請求體。
{
"code": "0",
"message": "success",
"data": null
}
其他就不一一測試了,感興趣的可以自己嘗試下。