在实际开发中,需要对字段进行效验,通过使用if-else对请求的每一个参数一一效验,当效验的参数过多时,会有很多的if-else,代码不简洁,我们可以使用Spring提供的Validated,通过注解的形式完成效验。
注: SpringBoot 2.3.x 移除了validation依赖需要手动引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
当效验不通过时,会抛出异常,这时可以使用统一异常处理对返回格式进行处理。
@Slf4j
@ControllerAdvice
public class ValidatedExceptionHandler {
/**
* 对于表单提交有效
*/
@ResponseBody
@ExceptionHandler(BindException.class)
public String BindExceptionHandler(BindException e) {
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
List<String> msgList = new ArrayList<>();
for (ObjectError allError : allErrors) {
msgList.add(allError.getDefaultMessage());
}
return "【BindException】异常:" + msgList.toString();
}
/**
* 对提交的方式为json格式有效
*/
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public String exceptionHandler(MethodArgumentNotValidException exception) {
BindingResult result = exception.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
if (errors != null) {
errors.forEach(p -> {
FieldError fieldError = (FieldError) p;
stringBuilder.append(fieldError.getDefaultMessage());
});
}
}
return "【MethodArgumentNotValidException】异常:" + stringBuilder.toString();
}
/**
* 对@RequestParam参数加@NotBlank、@NotNull、NotEmpty等
*/
@ResponseBody
@ExceptionHandler(value = ConstraintViolationException.class)
public String ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
List<String> msgList = new ArrayList<>();
while (iterator.hasNext()) {
ConstraintViolation<?> cvl = iterator.next();
msgList.add(cvl.getMessageTemplate());
}
return "【ConstraintViolationException】异常:" + msgList.toString();
}
}
- ConstraintViolationException:对@RequestParam参数加@NotBlank、@NotNull、NotEmpty等
@Validated
@RestController
@RequestMapping("/text")
public class TextController {
@GetMapping("/validationNotEmpty")
public String validationNotEmpty(@RequestParam("detailId") @NotEmpty(message = "detailId is not null") String detailId) {
return "success";
}
}
测试
- MethodArgumentNotValidException:对提交的方式为json格式有效
@RestController
@RequestMapping("/text")
public class TextController {
@PostMapping("/validationNotEmpty")
public String validationNotEmpty(@RequestBody @Validated OrderDetail orderDetail) {
System.out.println(orderDetail.toString());
return "success";
}
}
类
@Data
public class OrderDetail implements Serializable {
private static final long serialVersionUID = 8105326778623039417L;
@NotEmpty(message = "name is not null")
private String name;
@NotNull(message = "count is not null")
@Max(value = 0, message = "不能为负数")
private Integer count;
@NotEmpty(message = "detail is not null")
private String detail;
}
测试
- BindException:对于表单提交有效
@RestController
@RequestMapping("/text")
public class TextController {
@PostMapping("/validationNotEmpty")
public String validationNotEmpty(@Validated OrderDetail orderDetail) {
System.out.println(orderDetail.toString());
return "success";
}
}
测试
常用注解:
分组
当多个接口使用同一个实体类,传的参数不同则效验也不同,或者有一些特殊的参数只有一个接口被使用,可以通过分组不同的接口使用不同的效验参数。
组:OneGroup
public interface OneGroup extends Default {}
组:TwoGroup
public interface TwoGroup extends Default {}
类
@Data
public class OrderDetail implements Serializable {
private static final long serialVersionUID = 8105326778623039417L;
@NotEmpty(groups = OneGroup.class, message = "name is not null")
private String name;
@NotNull(groups = TwoGroup.class, message = "count is not null")
private Integer count;
@NotEmpty(message = "detail is not null")
private String detail;
}
使用
@RestController
@RequestMapping("/text")
public class TextController {
@PostMapping("/validationNotEmpty")
public String validationNotEmpty(@Validated({OneGroup.class}) OrderDetail orderDetail) {
System.out.println(orderDetail.toString());
return "success";
}
}
测试,因为使用的是第一组,所以count没有被效验。而detail没有规定组所以一直会被效验。
使用TwoGroup
@RestController
@RequestMapping("/text")
public class TextController {
@PostMapping("/validationNotEmpty")
public String validationNotEmpty(@Validated({OneGroup.class, TwoGroup.class}) OrderDetail orderDetail) {
System.out.println(orderDetail.toString());
return "success";
}
}
测试,第二组也被使用,这时count也会被效验。
也可以使用@Valid注解来实现参数的效验。
@Valid和@Validated区别:
- @Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。而@Valid没有。
-
@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上。
@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上。
-
@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。
@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。