概述
使用Hibernate校验进行参数校验时,如果参数不符合校验规则,就会抛出异常。对于这些异常,我们不可能将完整的异常信息直接抛出去给前端,都是需要处理成一定格式给到前端,例如说哪个属性是什么错误
示例
package wxw.mengyuan.tool.exception;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.support.WebExchangeBindException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Hibernate校验的全局异常处理器
*/
@ControllerAdvice(annotations = { Controller.class, RestController.class })
@Slf4j
@ResponseBody
public class HibernateGlobalExceptionHandler2 {
/**
* hibernate-validator校验普通参数异常的处理
* 注意:快速失败返回模式下,参数名称为arg0、arg1...,并不是真实的形参名称
*/
@ExceptionHandler(value = { ConstraintViolationException.class })
private Object hibernateNormalValidExceptionHandler(Exception e) throws Exception {
// 打印日志
log.error("校验普通参数异常", e);
// 获取异常信息
Map<String, String> errorMessage = new HashMap<>();
for (ConstraintViolation<?> cv : ((ConstraintViolationException) e).getConstraintViolations()) {
// cv.getPropertyPath():得到"方法名.参数名称"
errorMessage.put(cv.getPropertyPath().toString().split("\\.")[1], cv.getMessage());
}
// 返回结果
return errorMessage;
}
/**
* hibernate-validator校验对象属性异常的处理
*/
@ExceptionHandler(value = { WebExchangeBindException.class, MethodArgumentNotValidException.class })
private Object hibernateObjectValidExceptionHandler(Exception e) throws Exception {
// 打印日志
log.error("校验对象属性异常", e);
// 获取异常信息
Map<String, String> errorMessage = new HashMap<>();
for (FieldError fieldError : ((BindingResult) e).getFieldErrors()) {
errorMessage.put(fieldError.getField(), fieldError.getDefaultMessage());
}
// 返回结果
return errorMessage;
}
/**
* 堆代码 duidaima.com
* hibernate-validator校验其他情况异常的处理
*/
@ExceptionHandler(value = { HttpMessageNotReadableException.class })
private Object hibernateEnumValidExceptionHandler(Exception e) throws Exception {
// 打印日志
log.error("校验枚举属性异常", e);
// 未传递参数
if (e.getCause() == null) {
return "参数不能为空";
}
// 获取异常信息
Map<String, String> errorMessage = new HashMap<>();
// 枚举值校验失败
if (e.getCause() instanceof InvalidFormatException cause) {
StringBuilder value = new StringBuilder("【" + cause.getValue() + "】不在【");
// 获取枚举类
Class<?> targetType = cause.getTargetType();
// 获取枚举类的所有字面量
Object[] enumConstants = targetType.getEnumConstants();
// 获取非静态的字段对象
Field[] declaredFields = Arrays.stream(targetType.getDeclaredFields()).filter(field -> !Modifier.isStatic(field.getModifiers())).toArray(Field[]::new);
// 获取其中标注了@JsonValue的字段
Field targetField = null;
for (Field field : declaredFields) {
if (field.isAnnotationPresent(JsonValue.class)) {
field.setAccessible(true);
targetField = field;
}
}
if (targetField == null) {
targetField = declaredFields[0];
targetField.setAccessible(true);
}
// 遍历字面量,获取对应的字段的值
for (int i = 0; i < enumConstants.length; i++) {
value.append(targetField.get(enumConstants[i]));
if (i < enumConstants.length - 1) {
value.append(", ");
}
}
value.append("】范围内");
errorMessage.put(cause.getPath().getFirst().getFieldName(), value.toString());
}
// 非对象属性名称注入失败
if (e.getCause() instanceof UnrecognizedPropertyException cause) {
errorMessage.put(cause.getPropertyName(), "属性名称不存在");
}
// 返回结果
return errorMessage;
}
}
处理后的错误信息
普通参数校验异常处理返回信息
{
"password": "密码不能为空",
"username": "账号不能为空"
}
对象参数校验异常处理返回信息
{
"password": "密码不能为空",
"phone": "手机号不能为空",
"username": "账号不能为空"
}
枚举属性校验异常
{
"gender": "【AA】不在【男, 女】范围内"
}