springboot使用validation-api对入参进行校验
前言:在实际开发中,我们不能完全相信前端给我们的参数是否符合预期或规范,为了避免恶意入参,引发系统或数据安全问题,需要对前端传过来的参数进行校验。例如字符串长度校验,空值校验,手机号校验,邮箱校验,字符串复杂性校验等等。
springboot提供了validation-api,对入参校验做了封装,方便使用。
1. 引入依赖
<!-- 入参校验 validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
2. 创建入参实体类dto
使用validation-api校验注解标注入参dto
@Data
@ToString
public class UserInfoDto {
@NotNull(message = "userId不能为空") // 非空
private String userId;
@NotBlank(message = "账号不能为空") // 非空字符串
private String account;
@Email(message = "邮箱格式不正确") // 邮箱格式
private String email;
@Length(min = 2, max = 20, message = "昵称长度不能低于2个字符且不能高于20个字符") // 字符串长度最小2,最大20
@NotNull(message = "昵称不能为空")
private String nickname;
@Range(min = 0, max = 130, message = "年龄区间为0-130岁") // 数值最小0,最大130
private Integer age;
@Past(message = "生日不能是未来时间") // 日期必须是过去的日期
private Date birthday;
}
注意点
非常重要的一点是:**以上注解是独立的,@Email不会去验证Email是否为空,就是说即便添加了@Email、@Lenght、@Range等等注解,它只是在前端传来的参数中,带有被该注解修饰的参数才去做校验。**例如前端给的json为{"userId":"123","account":"456","nickname":"zhangsan"}
,那么也是可以通过以上校验的,不会报校验不通过。
3. 接口类
在controller接收参数时还需要标注@Validated注解
@RestController
@RequestMapping("userinfo")
@Validated
public class UserInfoController {
@PostMapping("add1")
public String addUserInfo1(@Validated @RequestBody UserInfoDto userInfoDto){
System.out.println(userInfoDto);
return "添加成功";
}
@PostMapping("add2")
public String addUserInfo2(@Validated UserInfoDto userInfoDto){
System.out.println(userInfoDto);
return "添加成功";
}
@PostMapping("add3")
public String addUserInfo2(@Email String email){
System.out.println(email);
return "添加成功";
}
}
注意点:类上的@Validated注解是配合RequestParam上的@Email(也包含其他如@NotNull,@Length等注解)使用的,如果仅对RequestBody参数做验证,则直接在RequestBody前加@Validated即可(配合以上接口代码理解)
4. 异常处理类
以上代码对入参做了校验,结果是参数不符合直接报错,将异常返回给前端,例如
在实际开发肯定是不合适的,我们需要对异常进行捕获,返回前端能识别的异常信息。就需要用到Springboot提供的全局异常捕获处理机制。创建异常处理类:
@ControllerAdvice
public class ValidateHandler {
/**
* 异常处理方法,符合ExceptionHandler.value的异常,会执行其处理方法,
* 处理的是RequestBody校验的异常
* @param e 捕获的异常
* @return 返回前端的数据
*/
@ExceptionHandler(value = org.springframework.validation.BindException.class)
@ResponseBody
public String handleBindException(Exception e) {
org.springframework.validation.BindException ex = (org.springframework.validation.BindException) e;
String collect = ex.getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining("; "));
// 实际开发中一般返回带状态码的Result对象,这里仅做简单演示,自行修改
return collect;
}
/**
* 处理的是RequestParam校验的异常
*/
@ExceptionHandler(value = javax.validation.ConstraintViolationException.class)
@ResponseBody
public String handleConstraintViolationException(Exception e) {
javax.validation.ConstraintViolationException ex = (javax.validation.ConstraintViolationException) e;
String collect = ex.getConstraintViolations().stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("; "));
return collect;
}
}