你还在手动对数据进行校验,快来使用validation吧

2023-11-03

       本篇主要讲解使用javax.validation.constraints,org.hibernate.validator.constraints下的校验方法对实体类进行自动校验, 直接对数据进行校验,通过对接收的数据进行校验,如果不符合我们定义的要求则会提示对应的message信息,具体怎么做的,下面一步步来说明。

初步介绍校验

先看下javax.validation.constraints下面的一些数据校验方法:

AssertFalse 
AssertTrue
DecimalMax
DecimalMin
Digits
Email
Future
FutureOrPresent
Min
Max
Negative
NegativeOrZero
NotBlank
NotEmpty
NotNull
Null
Past
PastOrPresent
Pattern
Positive
PositiveOrZero
Size

再来看下org.hibernate.validator.constraints部分校验方法

Length
URL
Range
SafeHtml

具体怎么使用不是本节的重点,大家可以在需要用到的时候参考源码获取网上查阅资料,下面据两个例子来讲解:

@Length(min = 1, max = 30, message = "规则描述字段长度需要在{min}和{max}之间", groups = {InsertGroup.class, UpdateGroup.class})
private String ruleDesc;
@ApiModelProperty("规则优先级")
@NotEmpty(message="规则优先级不可为空",groups = {InsertGroup.class,UpdateGroup.class})
@Pattern(regexp = RegexpUtils.NON_ZERO_NEGATIVE_INTEGERS_REGEXP,message = "规则优先级必须为正整数")
private String salience;

使用上面的校验需要引入依赖:

spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

如果是Spring Mvc,那可以直接添加hibernate-validator依赖

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.1.Final</version>
</dependency> 

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>

 javax.validation在spring-boot-starter-web包也已经被引入了

校验分组

如果需要分组,可以定义分组:

public interface UpdateGroup extends Update {
}
import org.apache.ibatis.annotations.Insert;

public interface InsertGroup extends Insert {
}

hibernate的校验模式

Hibernate Validator有以下两种验证模式:

1 、普通模式(默认是这个模式)

  普通模式(会校验完所有的属性,然后返回所有的验证失败信息)

2、快速失败返回模式

  快速失败返回模式(只要有一个验证失败,则返回)

默认是普通模式

配置校验模式:

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                .addProperty("hibernate.validator.fail_fast", "true")
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }
}

rest接口参数校验

对@PathVariable和@RequestParam参数进行校验,需要在入参声明约束的注释

@GetMapping
    public Result getByUri(@RequestParam @NotEmpty String uri) {
        List<GatewayRoutePO> list = gatewayRoutService.query(GatewayRouteQueryParam.builder().uri(uri).build());
        return Result.success(Optional.ofNullable(list).get().stream().findFirst());
    }
  @GetMapping(value = "/get/{id}")
    public Result get(@PathVariable(name = "id") @NotBlank String routId) {
        LOGGER.info("get with id:{}", routId);
        return Result.success(gatewayRoutService.getRouteByRouteId(routId));
    }

对于RequestBody的参数校验直接在实体参数前加上 Validated 或者javax.validation.Valid

  @PostMapping(value = "/conditions")
    public Result search(@Validated @RequestBody GatewayRouteQueryParam gatewayRouteQueryParam) {
        return Result.success(gatewayRoutService.query(gatewayRouteQueryParam));
    }

校验原理

 这里需要提到RequestResponseBodyMethodProcessor,这个类有两个作用,

1:用于解析@RequestBody标注的参数

2:处理ResponseBody标注方法的返回值

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

解析RequestBody标注参数的方法是resolveArgument

@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		if (binderFactory != null) {
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (arg != null) {
                //进行参数校验
				validateIfApplicable(binder, parameter);
                //校验不通过抛MethodArgumentNotValidException异常
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
				}
			}
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}

		return adaptArgumentIfNecessary(arg, parameter);
	}

 校验方法:AbstractMessageConverterMethodArgumentResolver#validateIfApplicable 获取Validated注解,根据注解参数和注解信息验证DataBinder#validate

	protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
			Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
			if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
				Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
				Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
				binder.validate(validationHints);
				break;
			}
		}
	}
	public void validate(Object... validationHints) {
		Object target = getTarget();
		Assert.state(target != null, "No target to validate");
		BindingResult bindingResult = getBindingResult();
		// Call each validator with the same binding result
		for (Validator validator : getValidators()) {
			if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
				((SmartValidator) validator).validate(target, bindingResult, validationHints);
			}
			else if (validator != null) {
				validator.validate(target, bindingResult);
			}
		}
	}

通过在参数上加注释校验参数是否符合条件, 实际上就是使用AOP拦截,Spring通过MethodValidationPostProcessor动态主从AOP切面,然后使用MethodValidationInterceptor对切点进行织入增强

public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
		implements InitializingBean {

	@Override
	public void afterPropertiesSet() {
       //创建切面
		Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
       //创建advisor进行增强
		this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
	}


}

自定义校验:

定义自定义约束,有三个步骤

  • 创建约束注解
  • 实现一个验证器
  • 定义默认的错误信息
自定义约束注解:
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Constraint(validatedBy = {MobileValidator.class})
@Retention(RUNTIME)
@Repeatable(Mobile.List.class)
public @interface Mobile {

    /**
     * 错误提示信息,可以写死,也可以填写国际化的key
     */
    String message() default "手机号码不正确";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        Mobile[] value();
    }
}
public class MobileValidator implements ConstraintValidator<Mobile, String> {

    /**
     * 手机验证规则
     */
    private Pattern pattern;

    @Override
    public void initialize(Mobile mobile) {
        pattern = Pattern.compile(mobile.regexp());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }

        return pattern.matcher(value).matches();
    }
}

测试自定义校验:

import org.springframework.util.CollectionUtils;
 
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Iterator;
import java.util.Set;
 
public class ValidatorUtil {
 
    private static final Validator validator= Validation.buildDefaultValidatorFactory().getValidator();
 
    public static <T> String validate(T object){
        Set<ConstraintViolation<T>> violationSet=validator.validate(object);
        if(violationSet!=null&&!CollectionUtils.isEmpty(violationSet)){
            Iterator<ConstraintViolation<T>> iterator=violationSet.iterator();
            StringBuffer sb=new StringBuffer(64);
            while(iterator.hasNext()){
                sb.append(iterator.next().getMessage()).append(";");
            }
            return sb.toString();
        }
        return null;
    }
}

配置全局校验

/**
 * 全局参数验证
 */
@Slf4j
@Order(1)
@ControllerAdvice
@RestController
public class ValidationException {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult ValidExceptionHandler(MethodArgumentNotValidException exception) throws Exception {
        BindingResult bindingResult = exception.getBindingResult();
        if (bindingResult.hasErrors()) {
            String defaultMessage = bindingResult.getFieldError().getDefaultMessage();
            return CommonResult.failed(defaultMessage);
        }
        return CommonResult.success("校验通过");
    }
}

在接口中使用数据校验:

@PostMapping("/save")
@ApiOperation(value = "保存规则数据")
public CommonResult saveCvbRule(@Validated(InsertGroup.class) @RequestBody Rule rule) 

如果校验不通过会向前端返回message校验失败信息

参考资料:基于springboot使用hibernate validator校验数据

hibernate-validation官网

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

你还在手动对数据进行校验,快来使用validation吧 的相关文章

随机推荐

  • 回归平方和 ESS,残差平方和 RSS,总体平方和 TSS

    https zhidao baidu com question 565190261749684764 html 回归平方和 ESS 残差平方和 RSS 总体平方和 TSS 总变差 TSS 被解释变量Y的观测值与其平均值的离差平方和 总平方和
  • 5.12 数据结构——哈夫曼编码

    在远程通讯中 要将待传字符转换成二进制的字符串 假设要传输的字符为 ABACCDA 若编码为 A 00 B 01 C 10 D 11 那么要传输的字符的编码为 00010010101100 若将编码设计为长度不等的二进制编码 即让待传字符串
  • 基于深度学习的人脸表情识别(一)

    第一篇博客就不用Markdown 什么鬼 来写了 今天主要是被老板一通说 然后说两月看10篇paper 算了 还是丫丫自己先多码码论文吧 再加之这几天有开博的想法 就索性一起开了 顺道总结下最近看的一篇中文的 Facial Expressi
  • 【CPU】常见术语解释

    interrupt service routine ISR 中断服务程序 中断 指当CPU正在处理某件事情时 外部发生的某一事件 如一个电平的变化 一个脉冲沿的发生或 定时器计数溢出等 请求CPU迅速去处理 于是CPU暂时中止当前的工作 转
  • 【机器学习基础】Python机器学习入门指南(全)

    前言 机器学习 作为人工智能领域的核心组成 是计算机程序学习数据经验以优化自身算法 并产生相应的 智能化的 建议与决策的过程 一个经典的机器学习的定义是 A computer program is said to learn from ex
  • 计算机转换汉子英语,中英文切换(电脑怎么切换拼音打字)

    在打字时需要输入英文怎么切换 用搜狗等各类拼音法的话 不用切换就可自由地 中英 非常简单 没有人不知道吧 方法是 汉字状态时 按回车键就是英文字母 此时按空格键是汉字 谁能告诉我小键盘旁边 也就是全角和半角的旁边的中文和英文符号 切换的 c
  • vue-element-admin/template登录Request failed with status code 405

    问题 vue element admin vue admin template 登录不上 报错Request failed with status code 405 解决 main js的开发配置production修改为开发模式 deve
  • opencv 识别长方形_使用OpenCV检测图像中的矩形

    本文实例为大家分享了OpenCV检测图像中矩形的具体代码 供大家参考 具体内容如下 前言 1 OpenCV没有内置的矩形检测的函数 如果想检测矩形 要自己去实现 2 我这里使用的OpenCV版本是3 30 矩形检测 1 得到原始图像之后 代
  • shell基础语法

    1 变量 语法 变量名 变量值 PS 两边不能有空格 1 使用变量 变量名 ex Name wendy 声明变量 echo Name 或 echo Name 输出变量 ps 花括号可选 2 只读变量 readonly 变量 3 删除变量 u
  • maven明明本地仓库有依赖包,还会远程下载的问题

    我今天在无网的环境下 打算进行maven编译打包 可是明明有本地仓库 也配置了本地仓库 但是还是会从远程下载 然后再各大网友的帮助下 百度 谷歌 查询到主要的原因是在本地仓库的每个依赖包都存在 remote repositories文件 直
  • layui多文件上传与下载示例

    第一次写文件上传 不知道怎么下手 幸好有万能layui 方便了很多 HTML div class layui body header span class layui body header title 产品文件上传 span div di
  • 对话用友网络副总裁、用友大易创始合伙人石磊:新的人力资源红利时代已经到来

    数科星球原创 作者丨苑晶 编辑丨十里香 在这个日新月异的科技时代 无论是企业还是个人 拥抱先进的技术工具以及前沿的思维成为建立差异化优势 取得长期价值的不二法门 在真实的场景中 企业内部的管理状况更为复杂 2023年 在全行业跑步奔向增长之
  • 用python编写一个弹球游戏

    用python编写一个简单的弹球游戏 这是学习python时用来练习的一个项目 作为笔记 最终是实现一个简单的弹球游戏 效果图如下 源代码 无限命版的弹球游戏python代码 from tkinter import 来源于python的标准
  • “互联网+”定义及相关概念解析

    来源 中国互联网技术联盟 2015年5月 6月 7月 国家密集性的发布了三大重型文件 中国制造2025 大众创业万众创新政策措施意见 互联网 行动指导意见 随后 中国互联网技术联盟邀请专家委员会对此专门进行了闭门研讨和解读梳理 并将梳理成果
  • 有关res://ieframe.dll/dnserrordiagoff_webOC.htm# http://www.51hainuo.cn

    有关这个 res ieframe dll dnserrordiagoff webOC htm http www 51hainuo cn 大部分的人应该会觉得是dns错误或者dns解析本机不正常 然后就去找杀毒软件来杀毒 网上我搜索的到的很多
  • html水平导航和垂直导航,简单却实用的CSS水平和垂直导航栏【演示/源码】

    说到CSS导航栏 各种漂亮炫酷的样式都应有尽有 不过本文要介绍的是简单却又很实用的导航栏 分为水平导航栏和垂直导航栏两种样式 适合初学者学习使用 以及一些对设计要求不高的网页使用 简单却实用的CSS水平和垂直导航栏 概述 这个简单的教程将教
  • IOException parsing XML document from class path resource [applicationContent.xml]; nested exception

    Spring报错问题 IOException parsing XML document from class path resource applicationContent xml nested exception is java io
  • 深度学习算法优化系列

    1 前言 这是Google在CVPR 2018上发表的一篇int8量化的论文 题目为 Quantization and Training of Neural Networks for Efficient Integer Arithmetic
  • 机器学习数据预处理——特征选择

    引言 在机器学习的训练过程中 总是会碰到样本大 特征多的数据集 而这些数据集里面的数据有些是用处很小甚至完全无用的 如果一组数据中的无用数据占比较大时 一方面会使得模型的训练时间变长 另一方面模型容易出现欠拟合现象 而如果一组数据中作用较小
  • 你还在手动对数据进行校验,快来使用validation吧

    本篇主要讲解使用javax validation constraints org hibernate validator constraints下的校验方法对实体类进行自动校验 直接对数据进行校验 通过对接收的数据进行校验 如果不符合我们定