SpringBoot优雅的抛出业务异常
通常在我们的项目中,在参数的校验,执行失败,校验返回值时业如果有业务异常,我们通常是原地进行异常的信息编写和抛出,如果业务异常使用频繁,代码看上去就很很杂乱。 我这里提供一种优雅抛出异常的方式
版本信息
springBoot 2.4.2 jdk11
具体步骤
- 定义一个异常接口
/** * @author lyne * @date 2022/9/11 */public interface ErrorCode { /** * 获取错误信息 * @return msg */ String getMsg();
/** * 获取错误码 * @return code */ int getCode();
}2.然后根据具体的业务领域创建一个或这多个的实现类,因为异常实体一旦定义,就不轻易修改,所以我推荐使用枚举类继承接口 下面随意定义基础异常实体
/** * @author lyne * @date 2022/9/11 */public enum BaseErrorCode implements ErrorCode { /** * 空值错误 */ NULLError(5000, "参数不能为空!");
private final int code; private final String msg;
@Override public String getMsg() { return this.msg; }
@Override public int getCode() { return this.code; }
BaseErrorCode(int code, String msg) { this.code = code; this.msg = msg; }
}- 在定义异常实体后,我们需要定义一个静态方法以便我们快速的使用 这里就写了个简单的方法集合,更多的功能还请结合实际需要增补
/** * @author lyne * @date 2022/9/11 */public class When {
public static <T> void emptyReturnError(T t, ErrorCode errorCode) { if (t == null) nullReturnError(null, errorCode); if (t instanceof String) { if (((String) t).length() == 0) { returnError(errorCode); } } if (t instanceof Collection) { if (((Collection<?>) t).isEmpty()) { returnError(errorCode); } } }
public static <T> void nullReturnError(T t, ErrorCode errorCode) { if (t == null) { returnError(errorCode); } }
public static void trueReturnError(boolean t, ErrorCode errorCode) { if (t) { returnError(errorCode); } }
public static void falseReturnError(boolean t, ErrorCode errorCode) { trueReturnError(!t, errorCode); }
public static void returnError(ErrorCode errorCode) { throw new BaseException(errorCode.getCode(), errorCode.getMsg()); }}- 我们可以直接对异常拦截器进行改造以便入参兼容异常枚举类,也可以像本文中所写的那样兼容原有的异常入参处理。
@ExceptionHandler(value = BaseException.class)public R<?> baseExceptionHandler(HttpServletRequest request, BaseException e) { log.error("请求地址:{}; 业务异常:{}", request.getRequestURI(), e.getMessage()); return R.error(e.getMessage());}统一JSON实体
/** * 前端返回体 * {"success": true, "code", 2000, "msg": "", "obj": null} * * @author lyne * @date 2022/8/18 */@Datapublic class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
public static final String DEFAULT_SUCCESS_MESSAGE = "请求成功"; public static final String DEFAULT_ERROR_MESSAGE = "请求失败";
private static final int CODE_SUCCESS = 2000; private static final int CODE_ERROR = 5000;
/** * 是否成功 */ private boolean success = true; /** * 状态码 */ private int code; /** * 返回信息 */ private String msg; /** * 返回实体 */ private T obj;
public static <T> R<T> error(String msg) { return error(CODE_ERROR, msg.trim().isEmpty() ? DEFAULT_ERROR_MESSAGE : msg); }
public static <T> R<T> error(int code, String msg) { return error(code, msg, null); }
public static <T> R<T> error(int code, String msg, T obj) { return create(code, msg.trim().isEmpty() ? DEFAULT_ERROR_MESSAGE : msg, obj); }
public static <T> R<T> success(String msg) { return success(CODE_SUCCESS, msg.trim().isEmpty() ? DEFAULT_SUCCESS_MESSAGE : msg); }
public static <T> R<T> success(int code, String msg) { return success(code, msg, null); }
public static <T> R<T> success(String msg, T obj) { return success(CODE_SUCCESS, msg, obj); }
public static <T> R<T> success(int code, T obj) { return success(code, "请求成功", obj); }
public static <T> R<T> success(int code, String msg, T obj) { return create(code, msg.trim().isEmpty() ? DEFAULT_SUCCESS_MESSAGE : msg, obj); }
private static <T> R<T> create(int code, String msg, T obj){ R<T> ajaxR = new R<>(); ajaxR.code = code; ajaxR.msg = msg; ajaxR.obj = obj; return ajaxR; }}- 到这里就完成了业务异常的改造工作,下面就是具体的使用 这里截取了摸鱼君的部分代码
/** * @author lyne * @date 2022/10/7 */public enum UserErrorCode implements ErrorCode { PASSWORD_MD5_LENGTH_ERROR(40001, "请使用MD5摘要算法对密码进行加密处理"), USERNAME_OR_PASSWORD_ERROR(40002, "账号或密码不正确"), USER_NOT_FOUND_ERROR(40003, "未找到该用户!"), ADMIN_USER_FORBID_DISABLE(40004, "管理员账户无法禁用"), USER_EMAIL_MAIL_CODE_ERROR(40004, "邮箱验证码不正确!"),
;
private final int code; private final String msg;
UserErrorCode(int code, String msg) { this.code = code; this.msg = msg; }
@Override public String getMsg() { return this.msg; }
@Override public int getCode() { return this.code; }} @Override public R<String> resetPassword(LoginDto userDto) { // 校验密码长度 When.trueReturnError(userDto.getPassword().length() != 32, UserErrorCode.PASSWORD_MD5_LENGTH_ERROR); String mailCode = redisService.getCacheObject(Constants.User.KEY_MAIL_CODE + userDto.getEmail()); When.emptyReturnError(mailCode, UserErrorCode.USER_EMAIL_MAIL_CODE_ERROR); When.trueReturnError(!mailCode.equals(userDto.getVerifyCode()), UserErrorCode.USER_EMAIL_MAIL_CODE_ERROR); QueryWrapper<User> queryWrapper = new QueryWrapper<>(); if (StrUtil.isNotBlank(userDto.getAccountId())) { queryWrapper.eq(User.T_ID, userDto.getAccountId()); } if (StrUtil.isNotBlank(userDto.getEmail())) { queryWrapper.eq(User.T_EMAIL, userDto.getEmail()); } User sysUser = getById(queryWrapper); When.nullReturnError(sysUser, UserErrorCode.USER_NOT_FOUND_ERROR); sysUser.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword())); updateById(sysUser); logout(); return R.success("重置密码成功!"); }