前言:
虽然hibernate已经支持了很多基于注解的参数校验,但随着业务的复杂,其丰富的注解往往还是无法满足我们一些特定的场景,所以有时需要我们根据业务场景自定义一些注解来实现特定的参数校验。
在项目中也需要对一些异常进行统一的管理,并返回前端特定的message,此时可以使用全局异常进行统一处理
正文
1.创建实体类
public class Request {
/**
* 年
* yyyy
*/
public interface YearValid{}
/**
* 月
* yyyy-MM
*/
public interface MonthValid{}
/**
* 日
* yyyy-MM-dd
*/
public interface DayValid{}
@StrYear(groups = YearValid.class)
@StrMonth(groups = MonthValid.class)
@StrDay(groups = DayValid.class)
private String queryTime;
}
2.创建上述自定义注解(@StrYear、@StrMonth、@StrDay,三者逻辑相似,此处只实现@StrMonth)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {StrMonthValidator.class})//指定该注解校验逻辑
public @interface StrMonth {
String message() default "请输入正确的查询时间";
Class<?>[] groups() default {};
Class<? extends Payload> [] payload() default { };
}
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* 对传入的 String类型的时间进行校验
*/
public class StrMonthValidator implements ConstraintValidator<StrMonth, String> {
/**
* 校验String类型的串是否符合月类型
*
* @param value
* @param context
* @return
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
SimpleDateFormat format = null;
boolean b = true;
if (null != value && value.replace(" ", "").length() == 7) {
format = new SimpleDateFormat("yyyy-MM");
try {
format.parse(value);
} catch (ParseException e) {
b = false;
}
} else {
b = false;
}
return b;
}
}
3.controller层使用@Validated注解对实体属性进行校验
因业务中对于queryTime字段在不同接口中有不同的定义,所以使用了validator的groups分组模式,以便适配不同的情况
/**
* 校验传入的参数是否为正确的月份
*/
@PostMapping("/test")
public String memberOverallSummary(@RequestBody @Validated(value = RequestVo.MonthValid.class) RequestVo vo) {
System.out.println(vo.toString());
return vo.toString();
}
4.到此,已经可以实现参数校验了,但我们还没有对异常进行捕捉,此时,实现自定义异常,下边代码块中的ApiError为字定义的异常实体类
/**
* 处理所有接口数据验证异常
*
* @param e
* @returns
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
// 打印堆栈信息
log.error(e.getMessage(), e);
String msg = "校验失败: ";
BindingResult bindingResult = e.getBindingResult();
for (FieldError error : bindingResult.getFieldErrors()) {
msg += error.getDefaultMessage() + ";";
}
ApiError apiError = new ApiError(BAD_REQUEST.value(), msg);
return buildResponseEntity(apiError);
}
5.此时访问我们的接口,已经实现了参数校验,并且可以返回友好的异常提示信息
对于入参的queryTime参数我们不传或者是传空,或者传入的数据不符合时间格式,都会抛出我们希望的异常
{
"code": 400,
"msg": "校验失败: 请输入正确的查询时间;",
"data": null,
"success": false
}
PS:最后,如果我们有多个异常,希望校验器校验到第一个参数异常时立即返回,可以配置fail-fast,配置如下
/**
* 参数校验 fast-fail配置类
*/
@Configuration
public class ValidatorConfig {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure() // true-快速失败返回模式 false-普通模式
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
至此,我们已经可以优雅的实现参数校验和异常处理了