JSR-303规范对请求参数校验,以及异常统一处理,内嵌对象的属性校验
1.引入依赖,版本需要对应:其中javax.validation是定义规范的一些接口,而实现由hibernate-validator实现
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.7.Final</version> </dependency>
2.创建一个请求对象:
public class OrderRequest { private Long id; @NotBlank(message ="名称不能为空" ) private String name; @Pattern(regexp = "http://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?",message = "url路径不对") private String url; @Email(regexp="/+d/",message = "邮件格式不对") private String email; @DecimalMax("20") @DecimalMin("10") private BigDecimal amount; }
3.contoller实现:
@RequestMapping("/order") @ResponseBody public Map<String, String> testValid(@Valid @RequestBody OrderRequest orderRequest, BindingResult bindingResult){ boolean hasFieldErrors = bindingResult.hasFieldErrors(); Map<String, String> map = new HashMap<>(); if(hasFieldErrors){ for (FieldError fieldError : bindingResult.getFieldErrors()) { String field = fieldError.getField(); String defaultMessage = fieldError.getDefaultMessage(); map.put(field,defaultMessage); } map.put("errorCode","400"); return map; } map.put("errorCode","200"); return map; }
4.postman调用:
问题1:当所给的注解都满足不了需求的时候,如何自定义注解:需求:校验姓名是否带有gogo,参考注解:
参考上面NotBlank注解,定义一个Checkgogo注解:
@Documented @Constraint(validatedBy = { }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) public @interface CheckGogo { String message() default "{com.yang.xiao.hui.thymeleaf.controller.CheckGogo.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; String value(); }
我们需要定义一个使得该注解生效的类,点击@Constraint注解,看看需要一个怎么样的类:
实现如下:
public class MyGogoConstraintValidator implements ConstraintValidator<CheckGogo,String> { private String needIncludeValue; @Override public void initialize(CheckGogo constraintAnnotation) { needIncludeValue = constraintAnnotation.value(); //参数需要包含的字符 } @Override public boolean isValid(String value, ConstraintValidatorContext context) { return value.indexOf(needIncludeValue)!=-1 //校验参数是否包含该字符 } }
在注解上加上该实现类:
配置默认信息:在resources文件夹下新增:ValidationMessages.properties
测试:
postMan调用:
自定义提示语:
问题2:有些字段,我们在修改时希望不为空,在新增时希望为空,如编号,如何处理? 这里就要用到分组了,先定义2个空接口:
//新增分组 public interface AddCheck { } //修改分组 public interface UpdateCheck { }
在校验字段时加上分组字段:
修改controller:
测试:
问题3:上面所讲,校验失败后,每个controller都要自己处理校验失败的异常,如何统一校验:
定义一个异常处理类:
@RestControllerAdvice(basePackages="com.yang.xiao.hui.thymeleaf.controller") public class CommonExceptionProcessController { @ExceptionHandler(value = MethodArgumentNotValidException.class) public Map errorHandler(MethodArgumentNotValidException ex) { //处理参数校验异常 Map map = new HashMap(); BindingResult bindingResult = ex.getBindingResult(); for (FieldError fieldError : bindingResult.getFieldErrors()) { map.put(fieldError.getField(), fieldError.getDefaultMessage()); } map.put("code", 400); return map; } @ExceptionHandler(value = Throwable.class) public Map errorHandler(Throwable ex) { Map map = new HashMap(); map.put("code", 500); map.put("message", "服务挂了"); return map; } }
修改controller:
@RequestMapping("/order") @ResponseBody public BigDecimal testValid(@Validated(UpdateCheck.class) @RequestBody OrderRequest orderRequest){ //这里不用再单独处理bindResult了,只管业务代码即可 return new BigDecimal("20"); }
postMan调用:
//嵌套对象如何校验:
只要在内嵌对象添加一个@Valid注解即可