package com.yunhai.userbase.common; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.validation.BindException; 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 javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; /** * 全局异常处理器:仅处理「参数校验」类异常,把 Spring 默认抛出的冗长消息收敛成「一句话」返回, * 避免前端收到整段 Validation failed ... 堆栈文本。
* 返回 HTTP 200 + {@code {code:4, msg:..., data:null}} 结构(而非 HTTP 400), * 与前端 alova errorHandle 的约定一致:status!=200 会被当作网络错误,code=4 才走「参数错误」分支。
* 注意:本处理器【不】处理业务异常(如 {@code DException}),业务异常交由项目框架统一处理 * (返回 code=5、msg=异常消息),此处若拦截会覆盖框架行为。 * * @author chenx */ @RestControllerAdvice @Order(Ordered.HIGHEST_PRECEDENCE) public class GlobalExceptionHandler { /** 参数校验失败的业务码,前端按「参数错误」处理 */ private static final int CODE_PARAM_ERROR = 4; /** @RequestBody + @Validated/@Valid 失败:提取每个字段的 defaultMessage。 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Map handleMethodArgumentNotValid(MethodArgumentNotValidException ex) { return paramError(fieldErrorsMessage(ex.getBindingResult().getFieldErrors())); } /** 普通 Bean 参数(如 SortChildren/Move 的 @Validated 请求体对象)校验失败。 */ @ExceptionHandler(BindException.class) public Map handleBind(BindException ex) { return paramError(fieldErrorsMessage(ex.getBindingResult().getFieldErrors())); } /** 方法级约束(@PathVariable/@RequestParam 上的 @NotNull 等)校验失败。 */ @ExceptionHandler(ConstraintViolationException.class) public Map handleConstraintViolation(ConstraintViolationException ex) { String msg = ex.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining(";")); return paramError(msg); } private String fieldErrorsMessage(java.util.List fieldErrors) { if (fieldErrors == null || fieldErrors.isEmpty()) { return "参数校验失败"; } return fieldErrors.stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(";")); } /** HTTP 200 + code/msg/data 结构体,前端根据 code 提示 msg */ private Map paramError(String msg) { Map body = new LinkedHashMap<>(); body.put("code", CODE_PARAM_ERROR); body.put("msg", msg); body.put("data", null); return body; } }