在 Spring Boot 中,可以通过使用 @ControllerAdvice 注解和 @ExceptionHandler 注解来实现全局异常拦截。
@RestControllerAdvice 是 Spring Framework 提供的注解,用于定义全局异常处理类,并且结合 @ExceptionHandler 注解来处理异常。与 @ControllerAdvice 不同的是,@RestControllerAdvice 默认情况下会将返回值转换为 JSON 格式。
@RestControllerAdvice public class GlobalExceptionHandler { //.....拦截异常方法 }
@ResponseStatus(HttpStatus.BAD_REQUEST) 是一个注解,用于在异常处理方法上指定特定的HTTP状态码。当该异常被抛出时,将返回指定的HTTP状态码给客户端。
@RestControllerAdvice public class GlobalExceptionHandler { //.....拦截异常方法 /** * 缺少请求体异常处理器 * @param e 缺少请求体异常 使用get方式请求 而实体使用@RequestBody修饰 */ @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError); } }
@ExceptionHandler(...) 是一个异常处理注解,用于捕获请求的异常。当客户端发送的请求消息无法被框架正确解析时,将抛出该异常并调用对应的异常处理方法。
@RestControllerAdvice public class GlobalExceptionHandler { //.....拦截异常方法 /** * 缺少请求体异常处理器 * @param e 缺少请求体异常 使用get方式请求 而实体使用@RequestBody修饰 */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError); } }
RuntimeException 是 Java 提供的一个运行时异常类。与受检查异常不同,运行时异常不需要在方法声明中显式地声明或捕获,并且在运行时抛出时也不需要强制捕获或处理。所以我们可以在全局异常捕获中去捕获这个异常
public class BusinessException extends RuntimeException { private int code; //使用枚举构造 public BusinessException(HttpCodeEnum httpCodeEnum){ super(httpCodeEnum.getMsg()); this.code=httpCodeEnum.getCode(); } //使用自定义消息体 public BusinessException(HttpCodeEnum httpCodeEnum, String msg){ super(msg); this.code=httpCodeEnum.getCode(); } //根据异常构造 public BusinessException(HttpCodeEnum httpCodeEnum, Throwable msg){ super(msg); this.code=httpCodeEnum.getCode(); } }
上述代码定义了一个名为 BusinessException 的自定义异常类,它继承自 RuntimeException。
这个自定义异常类具有以下特点:
我们还需要一个类表示 HTTP 响应的状态码和对应的消息 ,以下为基本的举例查考。
public enum HttpCodeEnum { // 成功 SUCCESS(200, "操作成功"), // 登录 NEED_LOGIN(401, "需要登录后操作"), NO_OPERATOR_AUTH(403, "无权限操作"), SYSTEM_ERROR(500, "出现错误"), USERNAME_EXIST(501, "用户名已存在"), PHONENUMBER_EXIST(502, "手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"), REQUIRE_USERNAME(504, "必需填写用户名"), CONTENT_NOT_NULL(506, "评论内容不能为空"), FILE_TYPE_ERROR(507, "文件类型错误"), USERNAME_NOT_NULL(508, "用户名不能为空"), NICKNAME_NOT_NULL(509, "昵称不能为空"), PASSWORD_NOT_NULL(510, "密码不能为空"), EMAIL_NOT_NULL(511, "邮箱不能为空"), NICKNAME_EXIST(512, "昵称已存在"), LOGIN_ERROR(505, "用户名或密码错误"); 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; } }
上述代码定义了一个 HttpCodeEnum 枚举类,用于表示 HTTP 响应的状态码和对应的消息。
这个枚举类具有以下特点:
该类的主要作用是封装接口返回的数据,统一格式化输出,方便前端调用和展示。
import lombok.Data; import java.io.Serializable; @Data public class ResponseResultimplements 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; } }
全局异常捕获是一种处理应用程序中未处理的异常的机制,它可以统一处理应用程序中的异常,避免异常导致程序崩溃或向用户显示不友好的错误信息。我们可以通过上述的解释去捕获异常,定义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="系统出错"; /** * 缺少请求体异常处理器 * @param e 缺少请求体异常 使用get方式请求 而实体使用@RequestBody修饰 */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError); } /* * @Description: 捕获请求方法异常,比如post接口使用了get */ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResponseResult methodNotAllowedHandler(HttpRequestMethodNotSupportedException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',请求方法不被允许'{}'", requestURI, e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError); } // get请求的对象参数校验异常 @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({MissingServletRequestParameterException.class}) public ResponseResult bindExceptionHandler(MissingServletRequestParameterException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',get方式请求参数'{}'必传", requestURI, e.getParameterName()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError); } // post请求的对象参数校验异常 @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({MethodArgumentNotValidException.class}) public ResponseResult methodArgumentNotValidHandler(MethodArgumentNotValidException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',post方式请求参数异常'{}'", requestURI, e.getMessage()); ListallErrors = e.getBindingResult().getAllErrors(); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), getValidExceptionMsg(allErrors)); } // 业务类异常 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(BusinessException.class) public ResponseResult businessExceptionHandler(BusinessException e) { String requestURI = httpServletRequest.getRequestURI(); System.out.println(e); log.error("请求地址'{}',捕获业务类异常'{}'", requestURI,e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage()); } // 运行时异常 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(RuntimeException.class) public ResponseResult runtimeExceptionHandler(RuntimeException e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',捕获运行时异常'{}'", requestURI, e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage()); } // 系统级别异常 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Throwable.class) public ResponseResult throwableExceptionHandler(Throwable e) { String requestURI = httpServletRequest.getRequestURI(); log.error("请求地址'{}',捕获系统级别异常'{}'", requestURI,e.getMessage()); return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage()); } private String getValidExceptionMsg(List 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; } }
发出请求
返回结果
捕获异常
发起请求
返回结果
捕获异常
发送请求
返回结果
捕获异常