SpringBoot的全局異常攔截實(shí)踐過程
在 Spring Boot 中,可以通過使用 @ControllerAdvice 注解和 @ExceptionHandler 注解來實(shí)現(xiàn)全局異常攔截。
@RestControllerAdvice
@RestControllerAdvice 是 Spring Framework 提供的注解,用于定義全局異常處理類,并且結(jié)合 @ExceptionHandler 注解來處理異常。與 @ControllerAdvice 不同的是,@RestControllerAdvice 默認(rèn)情況下會(huì)將返回值轉(zhuǎn)換為 JSON 格式。
@RestControllerAdvice
public class GlobalExceptionHandler {
//.....攔截異常方法
}@ResponseStatus(...)
@ResponseStatus(HttpStatus.BAD_REQUEST) 是一個(gè)注解,用于在異常處理方法上指定特定的HTTP狀態(tài)碼。當(dāng)該異常被拋出時(shí),將返回指定的HTTP狀態(tài)碼給客戶端。
@RestControllerAdvice
public class GlobalExceptionHandler {
//.....攔截異常方法
/**
* 缺少請(qǐng)求體異常處理器
* @param e 缺少請(qǐng)求體異常 使用get方式請(qǐng)求 而實(shí)體使用@RequestBody修飾
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',請(qǐng)求體缺失'{}'", requestURI, e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
}
}@ExceptionHandler(...)
@ExceptionHandler(...) 是一個(gè)異常處理注解,用于捕獲請(qǐng)求的異常。當(dāng)客戶端發(fā)送的請(qǐng)求消息無法被框架正確解析時(shí),將拋出該異常并調(diào)用對(duì)應(yīng)的異常處理方法。
@RestControllerAdvice
public class GlobalExceptionHandler {
//.....攔截異常方法
/**
* 缺少請(qǐng)求體異常處理器
* @param e 缺少請(qǐng)求體異常 使用get方式請(qǐng)求 而實(shí)體使用@RequestBody修飾
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',請(qǐng)求體缺失'{}'", requestURI, e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
}
}RuntimeException
RuntimeException 是 Java 提供的一個(gè)運(yùn)行時(shí)異常類。與受檢查異常不同,運(yùn)行時(shí)異常不需要在方法聲明中顯式地聲明或捕獲,并且在運(yùn)行時(shí)拋出時(shí)也不需要強(qiáng)制捕獲或處理。所以我們可以在全局異常捕獲中去捕獲這個(gè)異常
public class BusinessException extends RuntimeException {
private int code;
//使用枚舉構(gòu)造
public BusinessException(HttpCodeEnum httpCodeEnum){
super(httpCodeEnum.getMsg());
this.code=httpCodeEnum.getCode();
}
//使用自定義消息體
public BusinessException(HttpCodeEnum httpCodeEnum, String msg){
super(msg);
this.code=httpCodeEnum.getCode();
}
//根據(jù)異常構(gòu)造
public BusinessException(HttpCodeEnum httpCodeEnum, Throwable msg){
super(msg);
this.code=httpCodeEnum.getCode();
}
} 上述代碼定義了一個(gè)名為 BusinessException 的自定義異常類,它繼承自 RuntimeException。
這個(gè)自定義異常類具有以下特點(diǎn):
- 包含了一個(gè)
code字段,用于表示異常的錯(cuò)誤碼。 - 提供了不同的構(gòu)造方法,以方便在拋出異常時(shí)指定錯(cuò)誤碼和錯(cuò)誤信息。
BusinessException(HttpCodeEnum httpCodeEnum)構(gòu)造方法使用枚舉類型HttpCodeEnum來設(shè)置異常的錯(cuò)誤碼和錯(cuò)誤信息。BusinessException(HttpCodeEnum httpCodeEnum, String msg)構(gòu)造方法使用自定義的錯(cuò)誤信息來設(shè)置異常的錯(cuò)誤碼和錯(cuò)誤信息。BusinessException(HttpCodeEnum httpCodeEnum, Throwable msg)構(gòu)造方法使用其他異常的實(shí)例來設(shè)置異常的錯(cuò)誤碼,并可選地提供通過Throwable獲取的錯(cuò)誤信息。
HttpCodeEnum枚舉類
我們還需要一個(gè)類表示 HTTP 響應(yīng)的狀態(tài)碼和對(duì)應(yīng)的消息 ,以下為基本的舉例查考。
public enum HttpCodeEnum {
// 成功
SUCCESS(200, "操作成功"),
// 登錄
NEED_LOGIN(401, "需要登錄后操作"),
NO_OPERATOR_AUTH(403, "無權(quán)限操作"),
SYSTEM_ERROR(500, "出現(xiàn)錯(cuò)誤"),
USERNAME_EXIST(501, "用戶名已存在"),
PHONENUMBER_EXIST(502, "手機(jī)號(hào)已存在"), EMAIL_EXIST(503, "郵箱已存在"),
REQUIRE_USERNAME(504, "必需填寫用戶名"),
CONTENT_NOT_NULL(506, "評(píng)論內(nèi)容不能為空"),
FILE_TYPE_ERROR(507, "文件類型錯(cuò)誤"),
USERNAME_NOT_NULL(508, "用戶名不能為空"),
NICKNAME_NOT_NULL(509, "昵稱不能為空"),
PASSWORD_NOT_NULL(510, "密碼不能為空"),
EMAIL_NOT_NULL(511, "郵箱不能為空"),
NICKNAME_EXIST(512, "昵稱已存在"),
LOGIN_ERROR(505, "用戶名或密碼錯(cuò)誤");
int code;
String msg;
HttpCodeEnum(int code, String errorMessage) {
this.code = code;
this.msg = errorMessage;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
上述代碼定義了一個(gè) HttpCodeEnum 枚舉類,用于表示 HTTP 響應(yīng)的狀態(tài)碼和對(duì)應(yīng)的消息。
這個(gè)枚舉類具有以下特點(diǎn):
- 包含一組枚舉常量,每個(gè)常量代表一個(gè) HTTP 響應(yīng)狀態(tài)。
- 每個(gè)常量都有一個(gè)整型的
code和一個(gè)字符串類型的msg,分別表示狀態(tài)碼和對(duì)應(yīng)的消息。 - 提供了相應(yīng)的構(gòu)造方法、獲取
code和msg的方法。
ResponseResult類
該類的主要作用是封裝接口返回的數(shù)據(jù),統(tǒng)一格式化輸出,方便前端調(diào)用和展示。
import lombok.Data;
import java.io.Serializable;
@Data
public class ResponseResult<T> implements Serializable {
private Boolean success;
private Integer code;
private String msg;
private T data;
public ResponseResult() {
this.success=true;
this.code = HttpCodeEnum.SUCCESS.getCode();
this.msg = HttpCodeEnum.SUCCESS.getMsg();
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public static ResponseResult errorResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.error(code, msg);
}
public static ResponseResult okResult() {
ResponseResult result = new ResponseResult();
return result;
}
public static ResponseResult okResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.ok(code, null, msg);
}
public static ResponseResult setHttpCodeEnum(HttpCodeEnum enums) {
return okResult(enums.getCode(), enums.getMsg());
}
public ResponseResult<?> error(Integer code, String msg) {
this.success=false;
this.code = code;
this.msg = msg;
return this;
}
public ResponseResult<?> ok(Integer code, T data) {
this.success=true;
this.code = code;
this.data = data;
return this;
}
public ResponseResult<?> ok(Integer code, T data, String msg) {
this.success=true;
this.code = code;
this.data = data;
this.msg = msg;
return this;
}
public ResponseResult<?> ok(T data) {
this.success=true;
this.data = data;
return this;
}
}
全局異常捕獲
全局異常捕獲是一種處理應(yīng)用程序中未處理的異常的機(jī)制,它可以統(tǒng)一處理應(yīng)用程序中的異常,避免異常導(dǎo)致程序崩潰或向用戶顯示不友好的錯(cuò)誤信息。我們可以通過上述的解釋去捕獲異常,定義code類型枚舉返回ResponseResult給前端
import com.example.demo.util.HttpCodeEnum;
import com.example.demo.util.ResponseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@Autowired
private HttpServletRequest httpServletRequest;
private final String sysError="系統(tǒng)出錯(cuò)";
/**
* 缺少請(qǐng)求體異常處理器
* @param e 缺少請(qǐng)求體異常 使用get方式請(qǐng)求 而實(shí)體使用@RequestBody修飾
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',請(qǐng)求體缺失'{}'", requestURI, e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
}
/*
* @Description: 捕獲請(qǐng)求方法異常,比如post接口使用了get
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseResult methodNotAllowedHandler(HttpRequestMethodNotSupportedException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',請(qǐng)求方法不被允許'{}'", requestURI, e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
}
// get請(qǐng)求的對(duì)象參數(shù)校驗(yàn)異常
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({MissingServletRequestParameterException.class})
public ResponseResult bindExceptionHandler(MissingServletRequestParameterException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',get方式請(qǐng)求參數(shù)'{}'必傳", requestURI, e.getParameterName());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
}
// post請(qǐng)求的對(duì)象參數(shù)校驗(yàn)異常
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class})
public ResponseResult methodArgumentNotValidHandler(MethodArgumentNotValidException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',post方式請(qǐng)求參數(shù)異常'{}'", requestURI, e.getMessage());
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), getValidExceptionMsg(allErrors));
}
// 業(yè)務(wù)類異常
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(BusinessException.class)
public ResponseResult businessExceptionHandler(BusinessException e) {
String requestURI = httpServletRequest.getRequestURI();
System.out.println(e);
log.error("請(qǐng)求地址'{}',捕獲業(yè)務(wù)類異常'{}'", requestURI,e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
}
// 運(yùn)行時(shí)異常
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(RuntimeException.class)
public ResponseResult runtimeExceptionHandler(RuntimeException e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',捕獲運(yùn)行時(shí)異常'{}'", requestURI, e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
}
// 系統(tǒng)級(jí)別異常
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ResponseResult throwableExceptionHandler(Throwable e) {
String requestURI = httpServletRequest.getRequestURI();
log.error("請(qǐng)求地址'{}',捕獲系統(tǒng)級(jí)別異常'{}'", requestURI,e.getMessage());
return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
}
private String getValidExceptionMsg(List<ObjectError> errors) {
if(!CollectionUtils.isEmpty(errors)){
StringBuilder sb = new StringBuilder();
errors.forEach(error -> {
if (error instanceof FieldError) {
sb.append(((FieldError)error).getField()).append(":");
}
sb.append(error.getDefaultMessage()).append(";");
});
String msg = sb.toString();
msg = msg.substring(0, msg.length() -1);
return msg;
}
return null;
}
}
測(cè)試
入?yún)⒉徽_時(shí)

發(fā)出請(qǐng)求

返回結(jié)果

捕獲異常

運(yùn)行時(shí)錯(cuò)誤

發(fā)起請(qǐng)求

返回結(jié)果

捕獲異常

業(yè)務(wù)異常

發(fā)送請(qǐng)求

返回結(jié)果

捕獲異常

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java基于Rest?Assured自動(dòng)化測(cè)試接口詳解
Rest Assured 是一個(gè)基于 Java 的流行的用于測(cè)試 RESTful API 的庫。這篇文章主要介紹了Java如何基于Rest?Assured實(shí)現(xiàn)自動(dòng)化測(cè)試接口,需要的可以參考一下2023-03-03
Java數(shù)組使用binarySearch()方法查找指定元素的實(shí)現(xiàn)
這篇文章主要介紹了Java數(shù)組使用binarySearch()方法查找指定元素的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
spring使用xml方式整合Druid數(shù)據(jù)源連接池
傳統(tǒng)的JDBC數(shù)據(jù)庫連接方式,每次連接都需加載Connection到內(nèi)存并驗(yàn)證,使用后再放回,從而重復(fù)利用數(shù)據(jù)庫連接資源,這不僅降低了系統(tǒng)資源消耗,還避免了頻繁連接導(dǎo)致的服務(wù)器崩潰和內(nèi)存泄漏風(fēng)險(xiǎn),數(shù)據(jù)庫連接池在初始化時(shí)創(chuàng)建并保持最小數(shù)量的數(shù)據(jù)庫連接2024-10-10
Idea運(yùn)行單個(gè)main方法,不編譯整個(gè)工程的問題
這篇文章主要介紹了Idea運(yùn)行單個(gè)main方法,不編譯整個(gè)工程的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
JavaEE開發(fā)基于Eclipse的環(huán)境搭建以及Maven Web App的創(chuàng)建
本文主要介紹了如何在Eclipse中創(chuàng)建的Maven Project,本文是JavaEE開發(fā)的開篇,也是基礎(chǔ)。下面內(nèi)容主要包括了JDK1.8的安裝、JavaEE版本的Eclipse的安裝、Maven的安裝、Tomcat 9.0的配置、Eclipse上的M2Eclipse插件以及STS插件的安裝。2017-03-03
解決restlet client報(bào)錯(cuò)No response.Is the cer
這篇文章主要介紹了解決restlet client報(bào)錯(cuò)No response.Is the certificate valid? Click here to check.問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

