spring参数校验
Spring的参数校验遵从JSR303标准,spring为了给开发者提供便捷,对hibernate validation进行了二次封装,显示校验validated bean时, 你可以使用spring validation或者hibernate validation。一般在使用时,这两套API都是混着用。
Spring Boot引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
bean 的校验
定义对象如下:
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class DTO {
@NotNull(message ="id需要是20位以内的数字")
private Long id;
@NotBlank(message ="真实姓名不能为空")
@Length(max=20,message = "真实姓名最大长度为20")
private String name;
@NotBlank(message ="证件号码不能为空")
@Length(max=50,message = "证件号码最大长度为50")
private String identify;
}
Controller中的引用
public ResultMsg authentication(@Valid DTO dto,BindingResult bindingResult)
其中\@Valid 和BindingResult是一对一的,\@Valid 验证的结果会存放在BindingResult中。接下来对BindingResult的处理如下:
@RequestMapping("/foo")
public String foo(@Validated Foo foo, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
for (FieldError fieldError : bindingResult.getFieldErrors()) {
//...
}
return "fail";
}
return "success";
}
在获得验证的信息后,如果错误,则向客户端返回验证信息,无错误,继续处理。
方法中参数的校验
注入MethodValidationPostProcessor Bean
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
在需要方法验证的类上加注解 \@Validated
@Controller
@Validated
@RequestMapping("valid")
public class ValidController {
@RequestMapping("/check")
@ResponseBody
public String check(@Min(value = 2,message = "age必须大于2") int age) {
return "" + age;
}
}
方法验证会抛出ConstraintViolationException 异常,我们需要对ConstraintViolationException 做出统一处理,异常处理器定义如下:
异常处理器
@RestControllerAdvice
public class BindingExceptionHandler {
/**
* 用于处理入参绑定异常ConstraintViolationException
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handler(ConstraintViolationException e) {
StringBuilder strBuilder = new StringBuilder();
e.getConstraintViolations().stream().forEach(
violation -> {
strBuilder.append(violation.getMessage()+"\n");
}
);
return strBuilder.toString();
}
}
如果使用了\@Validated,那么Bean的校验也会抛出异常而不是之前的封装在BindingResult中。
校验规则
JSR提供的校验注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator提供的校验注解:
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
分组校验
有的时候,我们对一个实体类需要有多中验证方式,在不同的情况下使用不同验证方式,比如说对于一个实体类来的id来说,保存的时候 是不需要的,对于更新时是必须的,可以如下配置:
public class UserModel {
@NotNull(message = "{id.empty}", groups = { First.class })
private int id;
@NotNull(message = "{username.empty}", groups = { First.class, Second.class })
private String username;
@NotNull(message = "{content.empty}", groups = { First.class, Second.class })
private String content;
public interface First {}
public interface Second {}
}
在controller中的调用如下:
@RequestMapping(value = "/save.action", method = RequestMethod.POST)
public String save(@Validated( { Second.class }) UserModel userModel, BindingResult result) { …… }
@RequestMapping(value = "/update.action", method = RequestMethod.POST)
public String update(@Validated( { First.class, Second.class }) UserModel user, BindingResult result) { …… }
分组只能使用@Validated。
组序列
默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要,如下面两个例子:(1)第二个组中的约束 验证依赖于一个稳定状态来运行,而这个稳定状态是由第一个组来进行验证的。(2)某个组的验证比较耗时,CPU 和内存的使用率相对 比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。
在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。
public interface GroupA {}
public interface GroupB {}
@GroupSequence( { Default.class, GroupA.class, GroupB.class })
public interface Group {}
public class User {
@NotEmpty(message = "firstname may be empty")
private String firstname;
@NotEmpty(message = "middlename may be empty", groups = Default.class)
private String middlename;
@NotEmpty(message = "lastname may be empty", groups = GroupA.class)
private String lastname;
@NotEmpty(message = "country may be empty", groups = GroupB.class)
private String country;
}
controller中的验证如下:
@RequestMapping(value = "/update.action", method = RequestMethod.POST)
public String register(@Validated(Group.class) User user, BindingResult result)
{……}
验证多个对象
有时候我们需要传递多个验证的对象,通过如下形式验证。
@RequestMapping("/validate/multi")
public String multi(@Valid @ModelAttribute("a") A a, BindingResult aErrors, @Valid @ModelAttribute("b") B b,
BindingResult bErrors) {
if (aErrors.hasErrors()) {……}
if (bErrors.hasErrors()) {……}
}