jsr303,hibernate validator的注解使用教程和爬坑(一篇懂)

有目标就不怕路远。年轻人.无论你现在身在何方.重要的是你将要向何处去。只有明确的目标才能助你成功。没有目标的航船.任何方向的风对他来说都是逆风。因此,再遥远的旅程,只要有目标.就不怕路远。没有目标,哪来的劲头?一车尔尼雷夫斯基

导读:本篇文章讲解 jsr303,hibernate validator的注解使用教程和爬坑(一篇懂),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、前言

本文的目的是摸清jsr303注解的坑。

我对jsr303的态度是:能不用就不用。

原因:
1、坑多。不是所有开发人员都会花时间去摸细节
2、满足不了复杂校验。花拳绣腿,不能满足实际开发中复杂的校验场景,与其部分校验用jsr303,部分自写,还不如全部自写。

本文基于 6.0.17.Final 进行研究。其他版本是否会跟我的版本有出入? 未知(不过推测不可能有大的变化,不然以前用这个注解在升级后校验规则的行为变了,那就坑了)

二、使用注意

  • @Valid 和 @Validated,用@Valid,别用hibernate的注解,用javax的

    有些开发偶尔用@Valid,偶尔用@Validated,一个是javax的,一个是spring的注解。别这样,虽然@Validated有它独特的group功能,但是
    
  • 如下注意点

    // 不用@valid不检查
    @PostMapping("/test1")
    public String test1(T01_NoMessage dto) {
    	return "ok";
    }
    
    // 用了@Valid才检查
    @PostMapping("/test1")
    public String test1(@Valid T01_NoMessage dto) {
    	return "ok";
    }
    
    // 传JSON 可以生效
    @PostMapping("/2/test0")
    public Object test0(@Valid @RequestBody T50_Json dto) {
    	return dto;
    }
    
    // 不是dto类不生效(其他奇奇怪怪的入参不生效)
    @PostMapping("/test1")
    public String test1(@Valid List<T01_NoMessage> dto) {
    	return "ok";
    }
    
    // 不生效
    @PostMapping("/3/test1")
    public Object test1(@Valid @NotBlank String name) {
    	return "name:" + name;
    }
    
    
    // 生效:除了dto外还有其他字段、路径参数
    @PostMapping("/3/test4/{id}")
    public Object test4(
    	@PathVariable("id") int id,
    	String other,
    	@Valid T66_NotOnly dto
    ) {
    	return dto;
    }
    
    // 生效:传JSON,除了dto外,还有other。(PS:这个other怎么传参的? 通过`?other=xxx`即可)
    @PostMapping("/3/test5")
    public Object test5(
    	String other,
    	@Valid @RequestBody T66_NotOnly dto
    ) {
    	return dto;
    }
    
  • 嵌套类必须也用@Valid,否则不生效。详细的嵌套问题在后面单独讨论

    // A类中有B类,这就叫做内嵌
    
  • 正则表达式单和双斜杠都可以

    // 可以,两个等价的(建议用一个斜杠的,因为感觉比较正统)
    @Pattern(regexp = "^[\u4e00-\u9fa5a-zA-Z0-9_]{4,64}$")
    @Pattern(regexp = "^[\\u4e00-\\u9fa5a-zA-Z0-9_]{4,64}$")
    
  • Mockmvc测试controller的方法,jsr303正常生效;但是如果仅仅方法调用,不生效

    // 被校验
    @Test
    public void test2() throws Exception {
    	mockMvc.perform(MockMvcRequestBuilders.post("/3/test9").param("name", "stone")).andDo(MockMvcResultHandlers.print());
    }
    
    // 不被校验
    @Test
    public void test() {
    T69_SpringTest dto = new T69_SpringTest();
    	dto.setName(null);
    	t03_TestController.test9(dto);
    }
    
    // IDEA 的bug:IDEA自动补全时,会在变量前面生成@Valid注解
    @Test
    public void test() {
      // 这里的 @Valid 是代码自动补全的(IDEA 提示 `Create local variable 'dto'`),为什么会提示这个?
      // 实际上这个 @Valid 的注解,并不会触发拦截。实际上可能是 IDEA 的一个bug
      @Valid T69_SpringTest dto = new T69_SpringTest();
      //dto.setName("");
      dto.setName(null);
      t03_TestController.test9(dto);
    }
    

嵌套问题

注意,x-www-form-urlencorded的传参方式的dto是没有内嵌结构的,因为它是键值对传参,而键值对传参非常扁平,不会有嵌套结构。前端根本是不可能传这么复杂的、有层次结构的参数的。比如对于

public class A{
  private String name;
  private B b;
  private List<C> cList;
}

@PostMapping("/test")
public void post(@Valid A a) {
....
}

要怎么传参?
http://xxx?name=stone&b.name=cassie&cList.name=Pg1&cList.name=Pg2

这样吗? 没见过这么奇怪的传参

所以针对传JSON才有嵌套的说法

嵌套是什么?

嵌套是类中有类。比如A类有成员变量B类,或者B类的集合类,如

public class A {
  private B b;
  private List<C> cList;
}

以下是爬坑,注意点

  • 如何校验嵌套的类

    // 只用@NotNull只能管必填,但不会校验b或cList
    public class A {
      @NotNull
      private B b;
      @NotNull
      private List<C> cList;
    }
    
    // 之用@Valid只管校验,就是传了就校验,不传也行(因为允许null)
    public class A {
      @Valid
      private B b;
      @Valid
      private List<C> cList;
    }
    
    // 如果要校验 "必填且必须校验",则@Notnull和@Valid一起用
    public class A {
      @NotNull
      @Valid
      private B b;
      @NotNull
      @Valid
      private List<C> cList;
    }
    

    顺序

    Jsr303 注解了Controller的方法后,当发起http请求,如果检查不通过,会进入Controller方法吗? 会进入@ControllerAdvice的捕获方法吗?

  • 检测是在调用Controller方法之前,若不通过,是不会进入的。即打在controller方法的断点是不会执行的

  • jsr303检查抛出异常, 是能够在@ControllerAdvice里捕获的

    (打消疑惑:既然检查没进入Controller方法,那抛出异常时也就不会进入@ControllerAdvice的捕获方法里)

三、常用JSR303注解速查

注解 可以用在什么类型的字段上 作用
@NotBlank 只接受CharSequence 必填。非空串 “”,非trim后是空串的如 ” “
@NotEmpty 只接受CharSequence,Collection,Map,Array 必填。字串类型时不能是空串 “”,但可以是 ” “;集合类型时必须size大于0
@Notnull any types(即接受所有类型) 必填
@Max 只接受BigDecimal、BigInteger、byte、short、int、long、Byte、Short、Integer、Long;不支持double/float以及它的包装类 不必填,但填了就得符合
@Min 同@Max 不必填,但填了就得符合
@Size 只接受CharSequence,Collection,Map,Array 不必填,但填了就得符合
@Pattern 只接受CharSequence 不必填,但填了就得符合
@Email 只接受CharSequence 不必填,但填了就得符合
@Future 一系列日期的格式,详见补充1 不必填,但填了就得符合
@FutureOrPresent 同@Future 不必填,但填了就得符合
@Past 同@Future 不必填,但填了就得符合
@PastOrPresent 同@Future 不必填,但填了就得符合
  • 补充1:

    * java.util.Date
    * java.util.Calendar
    * java.time.Instant
    * java.time.LocalDate
    * java.time.LocalDateTime
    * java.time.LocalTime
    * java.time.MonthDay
    * java.time.OffsetDateTime
    * java.time.OffsetTime
    * java.time.Year
    * java.time.YearMonth
    * java.time.ZonedDateTime
    * java.time.chrono.HijrahDate
    * java.time.chrono.JapaneseDate
    * java.time.chrono.MinguoDate
    * java.time.chrono.ThaiBuddhistDate
    

四、一次生产爬坑和思考

记录一次生产中使用jsr303的疑惑和爬坑事件。

有一次产品说有个字段以前必填的,要改成选填。代码如下

@PostMapping("/2/test66")
public Object test66(@Valid @RequestBody T200_ProdSizeProblem dto) {
  return dto;
}


@Data
public class T200_ProdSizeProblem {
    @NotBlank
    private String name;

    @Valid
    private List<T200_ProdSizeProblemSub> subList;
}


@Data
public class T200_ProdSizeProblemSub {
    @NotBlank(message = "参数列描述不能为空")
    @Size(min = 1, max = 50, message = "最大长度为50")
    private String descr;
}

产品要的需求就是 descr 字段可选,于是我就把 @NotBlank(message = "参数列描述不能为空") 去掉,因为对于 @Size,我是知道它是 “非必填,但是如果填了长度就得符合”。所以我想你可以不填,但是填了就得按照我的长度要求,这样比较好。

但是出bug啦,界面上不填描述,但还是收到后端爆出的错误。

为什么啦? 之前不是说@Size 可以不填,但是填了才会进行size的校验的吗?

一排查,原来是描述不填的时候,desc还是传了空串。

作为后端很无奈,又不能要求前端同学 “在用户不填描述时不要出现descr字段,别传空串”,因为前端这部分逻辑比较复杂了所以不要求前端改,所以只能后端去掉@Size校验

PS:上述代码暴露出一个问题,以前的程序员根本就不知道要加上@NotNull,这个字段是一定要传的,并且size肯定会大于0,所以改成

@Data
public class T200_ProdSizeProblem {
    @NotBlank
    private String name;

  	@NotEmpty
    @Valid
    private List<T200_ProdSizeProblemSub> subList;
}

这就是我说的,很多同事根本就不清楚这些jsr303注释背后的细节和坑 !!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135292.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!