Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验
|
导包和配置 导入 JSR 303 的包、hibernate valid 的包 <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.5.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.0.Final</version> </dependency> springboot 配置 resources/application.yml 消息资源文件国际化处理配置 spring: encoding: UTF-8 # 必须指定解析编码,否则中文乱码 在 springboot 启动类里面配置
@SpringBootApplication
public class Application extends WebMvcConfigurerAdapter {
@Value("${spring.messages.basename}")
private String basename;
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
@Bean
@Primary
public MessageSource messageSource() {
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setUseCodeAsDefaultMessage(false);
resourceBundleMessageSource.setDefaultEncoding("UTF-8"); // 重复定义
resourceBundleMessageSource.setBasenames(basename.split(","));
return resourceBundleMessageSource;
}
@Bean
@Primary
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
validatorFactoryBean.setProviderClass(HibernateValidator.class);
validatorFactoryBean.setValidationMessageSource(messageSource());
return validatorFactoryBean;
}
@Override
public Validator getValidator() {
return validator();
}
/**
* 方法级别的单个参数验证开启
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
我们对于校验参数通过不了抛出的异常进行处理,是通过统一异常捕捉。
@ControllerAdvice
@Component
public class BindValidExceptionHandler {
@ResponseStatus(value = HttpStatus.OK)
@ExceptionHandler(ConstraintViolationException.class)
public @ResponseBody
Msg handleConstraintViolationException(ConstraintViolationException e) {
String messageTemplate = e.getConstraintViolations().iterator().next().getMessageTemplate();
return Msg.error(messageTemplate);
}
@ResponseStatus(value = HttpStatus.OK)
@ExceptionHandler(BindException.class)
public @ResponseBody
Msg handleBindException(BindException e) {
BindingResult bindingResult = e.getBindingResult();
String className = bindingResult.getTarget().getClass().getName();
FieldError next = bindingResult.getFieldErrors().iterator().next();
String fieldName = next.getField();
String defaultMessage = next.getDefaultMessage();
if (Pattern.compile("IllegalArgumentException: No enum").matcher(defaultMessage).find()) {
Matcher matcher = Pattern.compile("for value '(.*?)'").matcher(defaultMessage);
if (matcher.find()) {
defaultMessage = "找不到枚举类型【" + matcher.group(1) + "】";
}
}
return Msg.error(defaultMessage);
}
@ResponseStatus(value = HttpStatus.OK)
@ExceptionHandler(ValidError.class)
public @ResponseBody
Msg handleValidError(ValidError e) {
return Msg.error(e.getMessage());
}
}
resources/base.propertie creatorId=创建者 id 不能为小于 {value}。 modifierId=修改者 id 不能为小于 {value}。 resources/todo.properties todo.privateId.min=私有 id 不能为小于 {value}。 在 bean 字段上使用注解,其中 group 中的 C 和 S 接口是指 Controller 和 Service 的叫法简称,里面分别有 Insert 接口、Update 接口等等,都是自定义约定的东西。
/**
* 私有 id,是代表项目任务/非项目任务/风险/问题/评审待办问题等多张表的外键
*/
@Min(value = 1,message = "{todo.privateId.min}",groups = {C.Insert.class,C.Update.class,S.Insert.class,S.Update.class})
private long privateId;
/**
* 创建者id
*/
@Min(value = 1,message = "{creatorId}",groups = {S.Insert.class})
private long creatorId;
Controller 控制层验证
@Validated
@RestController
@RequestMapping("todo")
public class TodoController {
@Autowired
private TodoService todoService;
@GetMapping("getVo")
public Msg getVo(
@Min(value = 1,message = "待办 id 不能小于 1。")
@RequestParam(required = false,defaultValue = "0")
long id
) {
return this.todoService.getVo(id);
}
@PostMapping("add")
public Msg add(@Validated({C.Insert.class}) Todo todo) {
return this.todoService.add(todo);
}
}
@Validated({C.Insert.class}) 声明启用 bean 注解上的验证组,其他验证组不会进行验证,这样可以区别开来进行单独验证。 而像没有实体,只有一个基础数据类型的,可以进行验证,但是需要满足三个条件:
自行验证。 Service 服务层 AOP 验证 ValidUtil 工具类 需要被 springboot 扫描并注册为单例
@Component
public class ValidUtil {
@Autowired
private Validator validator;
public <T> Set<ConstraintViolation<T>> validate(T object,Class<?>... groups) {
return validator.validate(object,groups);
}
public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,String propertyName,Object value,Class<?>... groups) {
return validator.validateValue(beanType,propertyName,value,groups);
}
/**
* 校验参数,并返回第一个错误提示
* @param t 验证的对象
* @param groups 验证的组别
* @param <T> 对象擦除前原类型
* @return 第一个错误提示
*/
public <T> void validAndReturnFirstErrorTips(T t,Class<?>... groups) {
Set<ConstraintViolation<T>> validate = validator.validate(t,groups);
if (validate.size() > 0) {
ConstraintViolation<T> next = validate.iterator().next();
String message = next.getRootBeanClass().getName() + "-" + next.getPropertyPath() + "-" + next.getMessage();
throw new ValidError(message);
}
}
/**
* 校验参数,并返回第一个错误提示
* @param targetClass 验证的对象的 class 类型
* @param fieldName 需要验证的名字
* @param obj 需要属性值
* @param groups 验证的组别
* @param <T> 对象擦除前原类型
* @return 第一个错误提示
*/
public <T> void validAndReturnFirstErrorTips(Class targetClass,String fieldName,Object obj,Class<?>... groups) {
Set<ConstraintViolation<T>> validate = validator.validateValue(targetClass,fieldName,obj,groups);
if (validate.size() > 0) {
String message = targetClass.getName() + "-" + fieldName + "-" + validate.iterator().next().getMessage();
throw new ValidError(message);
}
}
}
AOP 配置 主要原理是利用 aop 拦截方法执行参数,对参数获取注解。再利用工具类来验证参数,如果验证不通过,直接抛出自定义错误,自定义错误已经全局统一处理了。
@Aspect
@Component
public class ValidatorAOP {
@Autowired
private ValidUtil validUtil;
/**
* 定义拦截规则:拦截 com.servic 包下面的所有类中,有 @Service 注解的方法。
*/
@Pointcut("execution(* com.service..*(..)) and @annotation(org.springframework.stereotype.Service)")
public void controllerMethodPointcut() {
}
/**
* 拦截器具体实现
*/
@Around("controllerMethodPointcut()") // 指定拦截器规则;也可以直接把 “execution(* com.xjj.........)” 写进这里
public Object Interceptor(ProceedingJoinPoint pjp) {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
Annotation[][] argAnnotations = method.getParameterAnnotations();
Object[] args = pjp.getArgs();
for (int i = 0; i < args.length; i++) {
for (Annotation annotation : argAnnotations[i]) {
if (Validated.class.isInstance(annotation)) {
Validated validated = (Validated) annotation;
Class<?>[] groups = validated.value();
validUtil.validAndReturnFirstErrorTips(args[i],groups);
}
}
}
try {
return pjp.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return true;
}
}
验证注解 @Min @NotNull 使用方法 不能写在实现类上,只能在接口中使用注解 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
