主题
统一异常处理与参数校验
一、自定义业务异常
业务规则校验失败(库存不足、用户名重复、余额不够)都属于业务异常,用一个统一异常类:
java
// common/BizException.java
package com.mall.common;
public class BizException extends RuntimeException {
public BizException(String message) {
super(message);
}
}业务代码里直接抛:
java
throw new BizException("库存不足");二、全局异常处理器
用一个 @RestControllerAdvice 捕获所有异常,统一转成 Result 返回,前端就能统一拿到 message:
java
// common/GlobalExceptionHandler.java
package com.mall.common;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
// 业务异常 → 返回业务错误消息
@ExceptionHandler(BizException.class)
public Result<Void> handleBiz(BizException e) {
log.warn("业务异常: {}", e.getMessage());
return Result.fail(e.getMessage());
}
// 参数校验失败 → 返回第一条校验消息
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValid(MethodArgumentNotValidException e) {
FieldError fieldError = e.getBindingResult().getFieldError();
return Result.fail(fieldError == null ? "参数错误" : fieldError.getDefaultMessage());
}
// 兜底异常 → 500,防止错误信息泄露到前端
@ExceptionHandler(Exception.class)
public Result<Void> handleOther(Exception e) {
log.error("系统异常", e);
return Result.fail("系统繁忙,请稍后重试");
}
}好处
- Controller 不用每个方法都写
try/catch。 - 错误信息统一格式,前端拦截器好统一提示。
- 兜底异常不把堆栈暴露给用户(安全),只记日志。
三、参数校验(Bean Validation)
DTO 上加注解,REST 章节 的 @Valid 就会自动触发校验。
java
// dto/RegisterDTO.java
package com.mall.dto;
import jakarta.validation.constraints.*;
import lombok.Data;
@Data
public class RegisterDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度 3~20 位")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 32, message = "密码长度 6~32 位")
private String password;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
}java
// dto/OrderCreateDTO.java
@Data
public class OrderCreateDTO {
@NotEmpty(message = "请选择要结算的商品")
private List<Long> cartIds;
@NotBlank(message = "收货地址不能为空")
private String address;
}常用校验注解:
| 注解 | 作用 |
|---|---|
@NotBlank | 字符串非空(去空格后) |
@NotEmpty | 集合/数组非空 |
@NotNull | 非 null |
@Size(min,max) | 字符串/集合长度 |
@Min / @Max | 数值范围 |
@Positive | 必须为正数 |
@Email | 邮箱格式 |
@Pattern | 正则匹配 |
四、Controller 启用校验
java
@PostMapping("/cart")
public Result<Void> add(@RequestBody @Valid CartAddDTO dto) {
cartService.add(userId(), dto);
return Result.ok(null);
}- 校验失败 → 抛
MethodArgumentNotValidException→ 被全局处理器捕获 → 返回第一条message。 - 前端 Axios 拦截器
ElMessage.error(res.message)就能弹出中文提示。
五、日志记录
@Slf4j 打印日志,线上排查问题就靠它:
java
@Slf4j
@Service
public class OrderService {
public Order create(...) {
log.info("用户 {} 创建订单,商品数 {}", userId, dto.getCartIds().size());
// 异常时
log.error("下单失败,userId={}, 原因={}", userId, e.getMessage());
}
}日志分级:
info(正常业务)、warn(可预期问题)、error(异常)。生产环境按级别分级存储。
六、验收
- [ ] 提交空用户名 → 返回
"用户名不能为空" - [ ] 提交错误手机号 → 返回
"手机号格式不正确" - [ ] 加超库存 → 返回
"xx 库存不足" - [ ] 故意制造异常 → 返回
"系统繁忙"且控制台有 error 日志