SpringBoot的全局異常攔截實(shí)踐過(guò)程
在 Spring Boot 中,可以通過(guò)使用 @ControllerAdvice 注解和 @ExceptionHandler 注解來(lái)實(shí)現(xiàn)全局異常攔截。
@RestControllerAdvice
@RestControllerAdvice 是 Spring Framework 提供的注解,用于定義全局異常處理類(lèi),并且結(jié)合 @ExceptionHandler 注解來(lái)處理異常。與 @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)碼給客戶(hù)端。
@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)客戶(hù)端發(fā)送的請(qǐng)求消息無(wú)法被框架正確解析時(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í)異常類(lèi)。與受檢查異常不同,運(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 的自定義異常類(lèi),它繼承自 RuntimeException。
這個(gè)自定義異常類(lèi)具有以下特點(diǎn):
- 包含了一個(gè)
code字段,用于表示異常的錯(cuò)誤碼。 - 提供了不同的構(gòu)造方法,以方便在拋出異常時(shí)指定錯(cuò)誤碼和錯(cuò)誤信息。
BusinessException(HttpCodeEnum httpCodeEnum)構(gòu)造方法使用枚舉類(lèi)型HttpCodeEnum來(lái)設(shè)置異常的錯(cuò)誤碼和錯(cuò)誤信息。BusinessException(HttpCodeEnum httpCodeEnum, String msg)構(gòu)造方法使用自定義的錯(cuò)誤信息來(lái)設(shè)置異常的錯(cuò)誤碼和錯(cuò)誤信息。BusinessException(HttpCodeEnum httpCodeEnum, Throwable msg)構(gòu)造方法使用其他異常的實(shí)例來(lái)設(shè)置異常的錯(cuò)誤碼,并可選地提供通過(guò)Throwable獲取的錯(cuò)誤信息。
HttpCodeEnum枚舉類(lèi)
我們還需要一個(gè)類(lèi)表示 HTTP 響應(yīng)的狀態(tài)碼和對(duì)應(yīng)的消息 ,以下為基本的舉例查考。
public enum HttpCodeEnum {
// 成功
SUCCESS(200, "操作成功"),
// 登錄
NEED_LOGIN(401, "需要登錄后操作"),
NO_OPERATOR_AUTH(403, "無(wú)權(quán)限操作"),
SYSTEM_ERROR(500, "出現(xiàn)錯(cuò)誤"),
USERNAME_EXIST(501, "用戶(hù)名已存在"),
PHONENUMBER_EXIST(502, "手機(jī)號(hào)已存在"), EMAIL_EXIST(503, "郵箱已存在"),
REQUIRE_USERNAME(504, "必需填寫(xiě)用戶(hù)名"),
CONTENT_NOT_NULL(506, "評(píng)論內(nèi)容不能為空"),
FILE_TYPE_ERROR(507, "文件類(lèi)型錯(cuò)誤"),
USERNAME_NOT_NULL(508, "用戶(hù)名不能為空"),
NICKNAME_NOT_NULL(509, "昵稱(chēng)不能為空"),
PASSWORD_NOT_NULL(510, "密碼不能為空"),
EMAIL_NOT_NULL(511, "郵箱不能為空"),
NICKNAME_EXIST(512, "昵稱(chēng)已存在"),
LOGIN_ERROR(505, "用戶(hù)名或密碼錯(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 枚舉類(lèi),用于表示 HTTP 響應(yīng)的狀態(tài)碼和對(duì)應(yīng)的消息。
這個(gè)枚舉類(lèi)具有以下特點(diǎn):
- 包含一組枚舉常量,每個(gè)常量代表一個(gè) HTTP 響應(yīng)狀態(tài)。
- 每個(gè)常量都有一個(gè)整型的
code和一個(gè)字符串類(lèi)型的msg,分別表示狀態(tài)碼和對(duì)應(yīng)的消息。 - 提供了相應(yīng)的構(gòu)造方法、獲取
code和msg的方法。
ResponseResult類(lèi)
該類(lèi)的主要作用是封裝接口返回的數(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)致程序崩潰或向用戶(hù)顯示不友好的錯(cuò)誤信息。我們可以通過(guò)上述的解釋去捕獲異常,定義code類(lèi)型枚舉返回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ù)類(lèi)異常
@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ù)類(lèi)異常'{}'", 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)文章
MyBatisPlus自定義SQL的實(shí)現(xiàn)
MyBatisPlus提供了自定義SQL功能,允許開(kāi)發(fā)者在Mapper接口中定義方法,并通過(guò)XML文件或注解編寫(xiě)SQL語(yǔ)句,本文詳解了如何在MP中使用自定義SQL,感興趣的可以了解一下2024-09-09
SpringBoot松散綁定/寬松綁定(Relaxed Binding)的實(shí)現(xiàn)
本文主要介紹了SpringBoot松散綁定/寬松綁定(Relaxed Binding)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
spring中對(duì)象注入的三種實(shí)現(xiàn)方式
本文主要介紹了spring中對(duì)象注入的三種實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
在java poi導(dǎo)入Excel通用工具類(lèi)示例詳解
這篇文章主要給大家介紹了關(guān)于在java poi導(dǎo)入Excel通用工具類(lèi)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09
Spring很常用的@Conditional注解的使用場(chǎng)景和源碼解析
今天要分享的是Spring的注解@Conditional,@Conditional是一個(gè)條件注解,它的作用是判斷Bean是否滿(mǎn)足條件,本文詳細(xì)介紹了@Conditional注解的使用場(chǎng)景和源碼,需要的朋友可以參考一下2023-04-04
SpringBoot自定義RestTemplate的攔截器鏈的實(shí)戰(zhàn)指南
在項(xiàng)目開(kāi)發(fā)中,RestTemplate作為Spring提供的HTTP客戶(hù)端工具,經(jīng)常用于訪問(wèn)內(nèi)部或三方服務(wù),但在實(shí)際項(xiàng)目中,我們往往需要對(duì)請(qǐng)求進(jìn)行統(tǒng)一處理,所以本文給大家介紹了SpringBoot自定義RestTemplate的攔截器鏈的實(shí)戰(zhàn)指南,需要的朋友可以參考下2025-07-07

