前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文学会JSR-303 参数校验,真香

一文学会JSR-303 参数校验,真香

作者头像
公众号 IT老哥
修改2020-09-21 16:32:18
1.5K0
修改2020-09-21 16:32:18
举报
文章被收录于专栏:用户7621540的专栏

本文源自 公-众-号 IT老哥 的分享

IT老哥,一个在大厂做高级Java开发的程序员,每天分享技术干货文章

早期的参数校验形式

在早期的时候,java的参数校验停留在获取参数之后在代码层面做校验,类似如下操作:

代码语言:javascript
复制
@PostMapping("/test")
public String test(@RequestBody TestRequest request) {
  if (StringUtils.isEmpty(request.getName())) {
      return "姓名不能为空";
  }
  if (request.getName().length() > 6) {
      return "姓名长度不能超过6";
  }
  if (StringUtils.isEmpty(request.getPhone())) {
      return "电话不能为空";
  }
  if (!isPhone(request.getPhone())) {
      return "电话号码格式不正确";
  }

  return "SUCCESS";
}
/**
 * 校验电话格式
 */
private boolean isPhone(String phone) {
  return true;
}

如代码中看到,每一种校验规则都需要在代码里面实现,那么光是参数校验也会耗费开发人员大量的精力,开发效率也会大幅降低

JSR-303简介

java6里面推出了一种规范:JSR-303,JSR是Java Specification Requests的缩写,意思是Java 规范提案,又叫做Bean Validation。

JSR 303是Java为bean数据合法性校验提供的标准框架。Hibernate Validator是Bean Validation的参考实现。

JSR-303参数校验注解

Bean Validation中内置的注解

Hibernate Validator 附加的注解

使用JSR303规范来做参数校验

我们将上面那个没使用JSR303的代码做改造

代码语言:javascript
复制
@Data
public class TestRequest {
  @NotEmpty(message = "姓名不能为空")
  @Length(min = 1, max = 6, message = "姓名长度必须在1-6之间")
  private String name;

  @NotBlank(message = "电话号码不能为空")
  @Pattern(regexp = "^134[0-8]\\d{7}$|^13[^4]\\d{8}$|^14[5-9]\\d{8}$|^15[^4]\\d{8}$|^16[6]\\d{8}$|^17[0-8]\\d{8}$|^18[\\d]{9}$|^19[8,9]\\d{8}$", message = "电话号码格式不正确")
  private String phone;

  @Email(message = "邮件格式不正确")
  private String email;

  @NotNull(message = "年龄不能为空")
  @Max(value = 18,message = "年龄不能超过18")
  private Integer age;
}

用@Valid对controller校验

代码语言:javascript
复制
@PostMapping("/test")
public String test(@RequestBody @Valid TestRequest request) {
    return "SUCCESS";
}

改造效果

@Valid与@Validated区别

首先看一下他们所属的包

可以看到@Validated属于spring,而@Valid属于javax

  • @Validated :org.springframework.validation.annotation.Validated
  • @Valid:javax.validation.Valid

但是在实际的基本使用中,这2者是没有区别的(注意这里说的是基本使用,也就是说,使用@Valid与@Validated是等价的)。

@Validated对controller校验

代码语言:javascript
复制
@PostMapping("/test")
public String test(@RequestBody @Validated TestRequest request) {
    return "SUCCESS";
}

将注解替换一下,也能得到相同的结果:

两个注解源码

  • @Valid
代码语言:javascript
复制
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE     })
@Retention(RUNTIME)
@Documented
public @interface Valid {
}
  • @Validated
代码语言:javascript
复制
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
  Class<?>[] value() default {};
}

通过源码可以看到@Validated可以在类上面使用,并且多了一个value属性

@Validated提供了一个分组功能,在校验参数时,可以根据不同的分组采用不同的校验机制。没有添加分组属性时,默认验证没有分组的验证属性。

@Validated分组校验

编写校验分组标识:

代码语言:javascript
复制
public class ValidatedGroup {
  public interface  Add extends Default {}
  public interface  Delete extends Default {}
  public interface  Update extends Default {}
  public interface  Query extends Default {}
}

测试请求类:

代码语言:javascript
复制
@Data
public class TestSaveRequest {

  @NotNull(groups = {ValidatedGroup.Update.class, ValidatedGroup.Delete.class}, message = "更新或者删除时id不能为空")
  private Long id;

  @NotBlank(groups = {ValidatedGroup.Update.class}, message = "更新时姓名不能为空")
  private String name;

  @NotNull(groups = {ValidatedGroup.Add.class}, message = "新增时余额不能为空")
  @Digits(integer = 10, fraction = 2)
  private BigDecimal balance;

  @NotBlank(groups = {ValidatedGroup.Add.class}, message = "新增时电话不能为空")
  @Pattern(regexp = "^134[0-8]\\d{7}$|^13[^4]\\d{8}$|^14[5-9]\\d{8}$|^15[^4]\\d{8}$|^16[6]\\d{8}$|^17[0-8]\\d{8}$|^18[\\d]{9}$|^19[8,9]\\d{8}$", message = "电话号码格式不正确")
  private String phone;

  @NotBlank(groups = {ValidatedGroup.Add.class}, message = "新增时邮件不能为空")
  @Email
  private String email;
}

根据我们编写的测试请求类型可预期:

  • 校验分组为Add时,会校验balance、phone、email三个请求字段
  • 校验分组为Update时,会校验id、name字段

测试如下:

代码语言:javascript
复制
@PostMapping("/testAdd")
public String testAdd(@RequestBody @Validated(value = ValidatedGroup.Add.class) TestSaveRequest request) {
    return "SUCCESS";
}

@PostMapping("/testUpdate")
public String testUpdate(@RequestBody @Validated(value = ValidatedGroup.Update.class) TestSaveRequest request) {
    return "SUCCESS";
}

testAdd结果:

testUpdate结果:

结果与预期完全符合。@Validated注解在分组校验时候,可以节省很多额外的开发,特别是当新增更新时。

一个需要传递主键一个不需要传递主键的情形,以前需要一个AddRequest、一个UpdateRequest,现在只有需要一个就够了。

嵌套校验

什么是嵌套校验呢?上代码:

代码语言:javascript
复制
@Data
public class TestNestRequest {

  @NotNull(message = "id不能为空")
  private Long id;

  @NotNull(message = "嵌套对象请求数据不能为空")
  private ItemRequest itemRequest;
}
代码语言:javascript
复制
@Data
public class ItemRequest {

  @NotEmpty(message = "name不能为空")
  private String name;
}
代码语言:javascript
复制
@PostMapping("/testNest")
public String testNest(@RequestBody @Valid TestNestRequest request) {
  return "SUCCESS";
}

测试结果如下:

只校验了id,没有校验嵌套对象中的name属性。

如果需要校验嵌套对象中的属性,需要改造一下TestNestRequest类。

itemRequest属性上加上@Valid注解,方能校验嵌套对象中的属性

改造如下:

代码语言:javascript
复制
@Data
public class TestNestRequest {

  @NotNull(message = "id不能为空")
  private Long id;

  @Valid
  @NotNull(message = "嵌套对象请求数据不能为空")
  private ItemRequest itemRequest;
}

测试结果:

一定要注意, 嵌套验证必须用@Valid注解。

云服务器云硬盘数据库(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos资源包,消息队列ckafka,点播资源包,实时音视频套餐,网站管家(WAF),大禹BGP高防(包含高防包及高防IP),云解析SSL证书,手游安全MTP移动应用安全云直播等等。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT老哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本文源自 公-众-号 IT老哥 的分享
  • 早期的参数校验形式
  • JSR-303简介
  • JSR-303参数校验注解
  • 使用JSR303规范来做参数校验
    • 改造效果
    • @Valid与@Validated区别
      • @Validated对controller校验
        • 两个注解源码
          • @Validated分组校验
          • 嵌套校验
          相关产品与服务
          实时音视频
          实时音视频(Tencent RTC)基于腾讯21年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档