JSR303校验(1)
前言
在讨论什么是JSR303之前,我们需要思考一下我们在日常开发中是如何对前端传过来的参数做校验的,这时候就会有人会问了:前端不是已经校验过数据了吗?为什么我们还要做校验呢,直接用不就好了?草率了,假如说前端代码校验没写好又或者是对于会一点编程的人来说,直接绕过前端发请求,把一些错误的参数传过来,你后端代码不就危险了嘛。所以我们一般都是前端一套校验,后端在一套校验,这样安全性就能够大大得到提升了。而没有没有接触过JSR303之前我们一般都是这样校验数据的:
显然这样的操作太过于繁琐,太多的if,else。而接触过JSR303之后我们可以这样来校验参数:
是不是很简单,废话不多说让我们开始学习JSR303吧。
什么是JSR303
JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint(约束) 的实现,除此之外还有一些附加的 constraint。
初体验
依赖
在pom.xml引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
实际上,spring-boot-starter-validation依赖主要是为了引入下面这个依赖:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.4.Final</version>
<scope>compile</scope>
</dependency>
使用
在我们的用来接收参数的类上加上hibernate-validator提供的注解
@Data
public class SysUser {
private Integer userId;
@NotEmpty(message = "密码不能为空")
@Size(min = 6, max = 20, message = "用户名长度在6-20位")
private String username;
@Min(value = 1, message = "年龄最小值为1")
@Max(value = 120, message = "年龄最大值为120")
private Integer age;
@Email(message = "邮箱格式不正确")
private String email;
@Past(message = "出身年月不能在未来")
private LocalDate birthday;
}
Controller代码,在类前面加上@Validated
@RestController
@RequestMapping("/sys/user")
public class SysUserController {
@PostMapping()
public Result saveSysUser(@Validated @RequestBody SysUser sysUser) {
return Result.success(HttpStatus.SUCCESS, "保存用户成功", sysUser);
}
}
测试
然后我们用api fox发送一下请求
先来个正常请求:
当然一切正常
接下来我们把用户名加长一点点OvO
很显然后端抛了异常,说明我们已经成功对参数做了校验,但是这样写依旧没有达到我们的目的,我们需要在参数校验失败之后给前端返回错误信息,所以我们还有要加一点点的代码。
改进代码
@PostMapping()
public Result saveSysUser(@Validated @RequestBody SysUser sysUser, BindingResult result) {
if (result.hasErrors()) {
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach(fieldError -> map.put(fieldError.getField(), fieldError.getDefaultMessage()))
;
return Result.error(HttpStatus.ERROR, "数据校验失败", map);
}
return Result.success(HttpStatus.SUCCESS, "保存用户成功", sysUser);
}
我们传参的时候再传一个BindingResult,顾名思义就是和数据捆绑的校验结果。一旦校验失败,我们就用一个map来接受出错的字段,这里用了jdk8的流来做遍历操作,当然用迭代器可以啦,然后返回给前端,这样前端也可以根据相应结果做出对应操作。
再次测试
这次我们的响应不会再报500了,而是把我们出错的字段显示了出来,对照我们之前写的代码是不是一下子就牛逼起来了(不是)。
总结
hibernate-validator可以帮助我们把繁多的校验ifelse去除,用注解来代替,简化我们的代码。但是现在这种校验我们需要在每个controller中都要写,这样冗余度还是太高了,所以我们可以搭配Spring提供的全局异常处理(RestControllerAdvice)来写的更加优雅。接着我们在做校验的时候,通常会遇到一个实体类的添加和修改,他们的校验规则是不同的,所以分组显得尤为重要。他可以帮助我们少建一个冗余的实体类。而分组和全局异常处理就留到下一次在聊吧,学习是个循序渐进的过程,慢慢来。