优雅的参数校验

摘要:
验证api<长度必须确保将属性注释@Size(max=64,@NotBlank(message=“type不能为空”)privateStringorderType添加到给定范围内的实体类中;当方法使用实体类输入参数时,添加验证注释publicRssave(@RequestBody@ValidatedMiTestEntitymiTest){returnRs.ok();

添加依赖

如果使用的是Springboot就不需要手动添加依赖了。Springboot已经依赖了。

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

参数检验的使用

注解名含义
AssertFalse带注释的元素必须为false
AssertTrue带注释的元素必须为true
DecimalMax带注释的元素必须是一个数字,其值必须小于或等于指定的最大值
DecimalMin带注释的元素必须是一个数字,其值必须大于或等于指定的最小值
Digits字段必须为数值,且正数部分不能超过 i 位,小数部分不能超过 j 位,null 元素被视为有效。
Email所注解的元素需满足Email格式
Future带注释的元素必须是将来的日期
FutureOrPresent字段必须为未来的时间或当前的时间
Past所注解的元素必须是某个过去的日期
PastOrPresent字段必须为过去的时间或当前的时间从
Max带注释的元素必须是一个数字,其值必须小于或等于指定的最大值
Min带注释的元素必须是一个数字,其值必须大于或等于指定的最小值
NotBlank所注解的元素值有内容
NotEmpty字段不能为 null 且不能为空,可以作用于字符串,其长度不能为 0,可作用于 Array、Collection、Map,其大小不能为 0
NotNull所注解的元素值不能为null
Null所注解的元素值为null
Pattern所注解的元素必须满足给定的正则表达式
Positive字段必须为正数,即数值大于 0
PositiveOrZero字段必须为正数或 0,即数值大于等于 0
Negative字段必须为负数,即数值小于 0
NegativeOrZero字段必须为负数或 0,即数值小于等于 0
Size所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
  • 实体类增加属性注解
    @Size(max= 64, message = "长度不能超过64")
    private String orderNo;
    @NotBlank(message = "类型不能为空")
    private String orderType;
    @Past(message = "创建时间在当前时间之前才可成功")
    private Date createDate;
    @Future(message = "更新时间在当前时间之后才可成功")
    private Date updateDate;
    @Email
    private String email;
    @Pattern(regexp = "^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$",message = "身份证格式不正确")
    private String idNum;
  • 方法使用实体类入参的时候增加校验注解
    public Rs save(@RequestBody @Validated MiTestEntity miTest) {
        return Rs.ok();
    }
    public Rs update(@RequestBody @Valid MiTestEntity miTest) {
        return Rs.ok();
    }
  • 使用全局异常拦截错误的字段校验
@RestControllerAdvice
public class BindExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Rs handleException(Exception e) {
        Rs r = new Rs();
        r.put("code" , 500);
        r.put("msg" , e.getMessage());
        return r;
    }
    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Rs handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return Rs.error(e.getBindingResult().getFieldError().getDefaultMessage());
    }
}

@NotNull, @NotEmpty和@NotBlank之间的区别是什么?

  • @NotEmpty

    • 不能是null
    • 不能是空字符
    • 集合框架中的元素不能为空
  • @NotNull

    • 被修饰元素不能为null,可用在基本类型上
  • @NotBlank

    • String不能是null,且去除两端空白字符后的长度0
  • @NotEmpty或者@NotBlank不可以用在基本数据类型上,会报错

    • rg.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type 'java.lang.Long'. Check configuration for 'offset'

@validated和@valid的区别

  • @Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验。
  • @Validated:Spring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制
  • 在Controller中校验方法参数时,使用@Valid和@Validated并无特殊差异,但是@Validated不可以嵌套校验,
public class A{
    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;
    @NotNull(message = "B不能为空")
    @Size(min = 1, message = "至少要有一个属性")
    private List<B> bList;
}
public class B{
    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;
    @NotBlank(message = "name不能为空")
    private String name;
}

//这样的情况下如果使用@Validated校验A的参数,那么bList泛型里面的属性则不会校验成功
//需要在private List<B> bList;上面加上@Valid来支持嵌套验证
  • @Validated注解可以用于类级别,用于支持Spring进行方法级别的参数校验。@Valid可以用在属性级别约束,用来表示级联校验
  • @Validated只能用在类、方法和参数上,而@Valid可用于方法、字段、构造器和参数上

自定义注解验证

某些业务场景下,上面的注解未必可以提供很好的校验,validation也为我们提供了自定义注解参数校验。

  • 注解
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {StringLengthValidImpl.class})
public @interface StringLength {
    /**
     * 在注解没有显示申明,则min值默认是 0
     */
    int min() default 0;
    /**
     * 在注解没有显示申明,则max值默认是18
     *
     * @return
     */
    int max() default 18;
    /**
     * 错误信息
     */
    String message() default "";

    /**
     * 分组
     */
    Class<?>[] groups() default {};

     //TODO 暂时不知道是干嘛的 看接口的注释是约束注解的负载(可用来保存一些数据)
    Class<? extends Payload>[] payload() default {};

    /**
     * 定义验证集合,可以为属性配置多套的验证规则
     */
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        StringLength[] value();
    }
}
  • 校验实现类
//<注解类,注解作用的参数类型(泛型)>
public class StringLengthValidImpl implements ConstraintValidator<StringLength, String> {

    private int min;
    private int max;
    private String message;
    /**
     * @Description: 初始化参数
     * @param: integerValid
     */
    @Override
    public void initialize(StringLength integerValid) {
        max = integerValid.max();
        min = integerValid.min();
        message = integerValid.message();
    }
    
    /**
     * @param: value 参数
     * @param: constraintValidatorContext 在应用给定的约束验证器时提供上下文数据和操作
     * @return: boolean
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        //禁止默认消息返回
        context.disableDefaultConstraintViolation();
        if (null == value) {
            context.buildConstraintViolationWithTemplate(message + ":参数值为空").addConstraintViolation();
            return false;
        } else if (value.length() > max) {
            context.buildConstraintViolationWithTemplate(message + ":不能超过" + max).addConstraintViolation();
            return false;
        } else if (value.length() <= min) {
            context.buildConstraintViolationWithTemplate(message + ":不能小于" + min).addConstraintViolation();
            return false;
        }
        return true;
    }
}
  • 分组校验
//用于新增校验
public interface save {
}
//用于修改校验
public interface update {
}
  • 参数校验
@Data
public class TestEntity {
    @StringLength.List({
            @StringLength(groups = {save.class}, message = "name",max = 10),
            @StringLength(groups = {update.class}, message = "name",max = 5)
    })
    private String name;
}
//不可以超过10个字符
    @PostMapping("add")
    public String add(@RequestBody @Validated({save.class}) TestEntity testEntity) {
        return JSON.toJSONString(testEntity);
    }
    //不可以超过5个字符
    @PostMapping("update")
    public String update(@RequestBody @Validated({update.class}) TestEntity testEntity) {
        return JSON.toJSONString(testEntity);
    }
    //没有限制
    @PostMapping("save")
    public String save(@RequestBody @Validated TestEntity testEntity) {
        return JSON.toJSONString(testEntity);
    }

免责声明:文章转载自《优雅的参数校验》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java——自定义外部字体文件Java 轻量开发框架 Solon 1.4.4 发布 ,完善分布式任务规范下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

pytest文档69-Hook函数之参数化生成测试用例pytest_generate_tests

前言 pytest 实现参数化有三种方式 pytest.fixture() 使用 fixture 传 params 参数实现参数化 @ pytest.mark.parametrize 允许在测试函数或类中定义多组参数,在用例中实现参数化 pytest_generate_tests 允许定义自定义参数化方案或扩展。 pytest_generate_tes...

CSS专题(三):页面高度发微

一 背景   本文不讨论普通元素的大小和位置,仅仅讨论页面的大小和位置,而且为了简化问题,本文只讨论高度与顶部距离。笔者发现,页面的宽度与高度的表现有很多不同,细心的朋友会发现,故页面宽度另起文讨论,文章已经写好,见页面宽度发微。   这里说的页面高度,是指浏览器页面的高度。有以下四种元素能够表现页面的高度:window,html,document,bod...

Java 迭代器删除元素ConcurrentModificationException异常。

Java是不支持容器类在使用迭代器迭代过程中,使用如 list.remove(obj)方法删除元素。否则会抛出ava.util.ConcurrentModificationException异常。应该使用iterator.remove()方法删除当前迭代到的元素。 这是因为Java集合中有一种叫fail-fast的机制,即如果多个线程对同一个集合的内容进行...

HTML超链接中文乱码

Vm中一个超链接URL需要拼接中文作为Get请求的参数。如果直接拼接,传到后台Action的参数对象中后取出会是乱码,需要编码后再拼接到URL上。  解决方法是在Action中添加一个成员变量,保存编码后的中文参数。在vm页面渲染时取出这个变量值,再拼接超链接。 在这里碰到的问题是:调用java.net.URLEncoder的encode()方法时,如...

数据库-求候选关键字

这类题目都是给定关系模型,求候选关键字.  题型: 这种给定关系模式和函数依赖的题目 ,做法大致有三步: 1、根据关系模式和函数依赖画出有向图. 2、找出是否有入度为0(即是没有任何元素可以推出他的元素),然后尝试是否可以从这个元素开始,随着箭头来遍历这个图,看看是否是哪一个元素都能遍历的到, 如果能够遍历的到的话,就可以判断这个关系的候选关键字为这个...

lua二进制操作函数

  由于 Lua 脚本语言本身不支持对数字的二进制操作(例如 与,或,非 等操作),MUSHclient 为此提供了一套专门用于二进制操作的函数,它们都定义在一个“bit”表中,使用时只要requre “bit”即可。 bit.ashr - 带符号的按位右移   此函数需要两个整数作为参数。第一个参数可以带有符号,是被以为的数,第二个参数是一个无符号整数,...