全局统一返回实体:
目的是除了数据信息,还要带上一些错误状态码,成功与否,错误信息等等以帮助更好理解可能的错误。
规约:默认的约定
异常码设计原则:
A客户端异常 B服务端异常 C远程调用异常
具体异常码设计架构
然后用枚举实现这个IErrorCode,在枚举常量中放入所有既定异常码和信息。
/*** 基础错误码定义*/
public enum BaseErrorCode implements IErrorCode {// ========== 一级宏观错误码 客户端错误 ==========CLIENT_ERROR("A000001", "用户端错误"),// ========== 二级宏观错误码 用户注册错误 ==========USER_REGISTER_ERROR("A000100", "用户注册错误"),USER_NAME_VERIFY_ERROR("A000110", "用户名校验失败"),USER_NAME_EXIST_ERROR("A000111", "用户名已存在"),USER_NAME_SENSITIVE_ERROR("A000112", "用户名包含敏感词"),USER_NAME_SPECIAL_CHARACTER_ERROR("A000113", "用户名包含特殊字符"),PASSWORD_VERIFY_ERROR("A000120", "密码校验失败"),PASSWORD_SHORT_ERROR("A000121", "密码长度不够"),PHONE_VERIFY_ERROR("A000151", "手机格式校验失败"),// ========== 二级宏观错误码 系统请求缺少幂等Token ==========IDEMPOTENT_TOKEN_NULL_ERROR("A000200", "幂等Token为空"),IDEMPOTENT_TOKEN_DELETE_ERROR("A000201", "幂等Token已被使用或失效"),// ========== 一级宏观错误码 系统执行出错 ==========SERVICE_ERROR("B000001", "系统执行出错"),// ========== 二级宏观错误码 系统执行超时 ==========SERVICE_TIMEOUT_ERROR("B000100", "系统执行超时"),// ========== 一级宏观错误码 调用第三方服务出错 ==========REMOTE_ERROR("C000001", "调用第三方服务出错");private final String code;private final String message;BaseErrorCode(String code, String message) {this.code = code;this.message = message;}@Overridepublic String code() {return code;}@Overridepublic String message() {return message;}
}
全局异常拦截器:
对所有的controller,处理一些未经捕获的异常,做一些特定处理
它的意义在于:比如你捕获了一个runtime exception,但是你并不知道具体是什么错误,也就不知道要用哪一个具体的异常码(上面设计的),所以这里自定义详细异常以对应不同的异常码,这样拦截器捕获什么自定义异常就返回什么对应异常码。
自定义一个抽象全局异常,然后三大类异常(分别对应三类异常码)继承抽象异常
示例:客户端异常,,构造是1.只自定义一个错误码 2.可以自定义信息 + 默认一级宏观客户端错误码 3. 自定义信息 + 具体客户端错误码 4.什么都要
service里抛异常
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.nageoffer.shortlink.admin.common.convention.errorcode.BaseErrorCode;
import com.nageoffer.shortlink.admin.common.convention.exception.AbstractException;
import com.nageoffer.shortlink.admin.common.convention.result.Result;
import com.nageoffer.shortlink.admin.common.convention.result.Results;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.Optional;/*** 全局异常处理器**/
@Component
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 拦截参数验证异常*/@SneakyThrows@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {BindingResult bindingResult = ex.getBindingResult();FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors());String exceptionStr = Optional.ofNullable(firstFieldError).map(FieldError::getDefaultMessage).orElse(StrUtil.EMPTY);log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr);return Results.failure(BaseErrorCode.CLIENT_ERROR.code(), exceptionStr);}/*** 拦截应用内抛出的异常*/@ExceptionHandler(value = {AbstractException.class})public Result abstractException(HttpServletRequest request, AbstractException ex) {if (ex.getCause() != null) {log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString(), ex.getCause());return Results.failure(ex);}log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString());return Results.failure(ex);}/*** 拦截未捕获异常*/@ExceptionHandler(value = Throwable.class)public Result defaultErrorHandler(HttpServletRequest request, Throwable throwable) {log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);return Results.failure();}private String getUrl(HttpServletRequest request) {if (StringUtils.isEmpty(request.getQueryString())) {return request.getRequestURL().toString();}return request.getRequestURL().toString() + "?" + request.getQueryString();}
}