1. Spring Validation JSR-303 简介 JSR-303 是 JavaEE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是 Hibernate Validator
。
此实现与 Hibernate ORM 没有任何关系。JSR-303 用于对 Java Bean 中的字段的值进行验证。 Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中使用注解的方式对表单提交的数据方便地验证。Spring 4.0 开始支持 Bean Validation 功能。在日常开发中,Hibernate Validator
经常用来验证bean的字段,基于注解,方便快捷高效。
2. JSR-303 基本的校验规则 2.1空检查
@Null
验证对象是否为 null
@NotNull
验证对象是否不为 null
, 无法查检长度为 0 的字符串
@NotBlank
检查约束字符串是不是 Null
还有被 Trim
的长度是否大于 0,只对字符串,且会去掉前后空格
@NotEmpty
检查约束元素是否为 NULL
或者是EMPTY
使用hibernate validator出现上面的错误, 需要注意@NotNull 和 @NotEmpty 和@NotBlank 区别
@NotEmpty 用在集合类上面 @NotBlank 用在String上面
@NotNull 用在基本类型上
在枚举类上不要加非空注解
2.2 布尔检查
@AssertTrue
验证Boolean
对象是否为 true
@AssertFalse
验证 Boolean
对象是否为false
2.3 长度检查
@Size(min=, max=)
验证对象(Array, Collection , Map, String)
长度是否在给定的范围之内
@Length(min=, max=)
验证字符串长度介于 min 和 max 之间2.4 日期检查
@Past
验证 Date
和 Calendar
对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
@Future
验证Date
和Calendar
对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期2.5 正则检查
@Pattern
验证 String
对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式
regexp
:正则表达式
flags
:指定Pattern.Flag
的数组,表示正则表达式的相关选项2.6 数值检查
注意: 建议使用在String
,Integer
类型,不建议使用在int
类型上,因为表单值为 “”
时无法转换为int
,但可以转换为String
为 “”
,Integer
为null
@Min
验证 Number 和 String 对象是否大等于指定的值
@Max
验证 Number 和 String 对象是否小等于指定的值
@DecimalMax
被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过 BigDecimal 定义的最大值的字符串表示.小数
存在精度
@DecimalMin
被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过 BigDecimal 定义的最小值的字符串表示 .小数
存在精度
@Digits
验证Number
和 String
的构成是否合法
@Digits(integer=,fraction=)
验证字符串是否是符合指定格式的数字,integer
指定整数精度,fraction
指定小数精度
@Range(min=, max=)
被指定的元素必须在合适的范围内
@Range(min=10000,max=50000,message=”range.bean.wage”)
@CreditCardNumber
信用卡验证
@Email
验证是否是邮件地址,如果为 null,不进行验证,算通过验证
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
2.7 对象校验
@Valid
被注释的元素是一个对象,需要检查此对象的所有字段值
@Validated
被注解的元素是一个对象或者一个类,需要检查此对象的所有字段值
3 使用 Spring Validation 验证 POM
这里我们使用 Hibernate Validator 5.x
来实现Spring Validation
接口,pom.xml 文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-validator</artifactId > <version > 5.3.4.Final</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency >
主要是增加了 org.hibernate:hibernate-validator
依赖
实体类参数校验 User 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import lombok.Data;import org.hibernate.validator.constraints.Email;import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;@Data public class User { @NotNull (message = "用户名id不能为空" ) private Long id; @NotNull (message = "用户账号不能为空" ) @Size (min = 6 , max = 11 , message = "账号长度必须是6-11个字符" ) private String account; @NotNull (message = "用户密码不能为空" ) @Size (min = 6 , max = 11 , message = "密码长度必须是6-16个字符" ) private String password; @NotNull (message = "用户邮箱不能为空" ) @Email (message = "邮箱格式不正确" ) private String email; }
controller
实体类上加上@Valid注解,使用json传参,加@RequestBody解析json参数映射为实体类,如果校验错误不会执行业务逻辑,抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RestController public class UserController { @Autowired private UserService userService; @PostMapping ("/addUser" ) public String addUser (@RequestBody @Valid User user) { return userService.save(user); } }
service 1 2 3 4 5 6 7 8 9 10 11 12 13 public interface UserService { String save (User user) ; } @Service public class UserServiceImpl implements UserService { @Override public String save (User user) { System.out.println("保存成功---->" +user); return "success" ; } }
PostMan测试 发送post
请求
1 2 3 4 5 6 { "id" : 1 , "account" : "12345678" , "password" : "12312123" , "email" : "15096021" # 错误格式 }
响应数据 数据不友好,使用全局异常捕获返回友好提示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 { "timestamp" : "2020-09-19T06:00:38.546+00:00" , "status" : 400 , "error" : "Bad Request" , "trace" : "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.util.List cn.zysheep.springboot_interface.controller.UserController.addUser(cn.zysheep.springboot_interface.entity.User): [Field error in object 'user' on field 'email': rejected value [15096021m]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@3796b0b3,.*]; default message [邮箱格式不正确]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:139)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:652)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n" , "message" : "Validation failed for object='user'. Error count: 1" , "errors" : [ { "codes" : [ "Email.user.email" , "Email.email" , "Email.java.lang.String" , "Email" ], "arguments" : [ { "codes" : [ "user.email" , "email" ], "arguments" : null , "defaultMessage" : "email" , "code" : "email" }, [], { "arguments" : null , "defaultMessage" : ".*" , "codes" : [ ".*" ] } ], "defaultMessage" : "邮箱格式不正确" , "objectName" : "user" , "field" : "email" , "rejectedValue" : "15096021m" , "bindingFailure" : false , "code" : "Email" } ], "path" : "/addUser" }
全局处理异常
全局处理异常,处理@RequestBody
参数校验异常,统一返回格式自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @RestControllerAdvice public class ExceptionControllerAdvice { @ExceptionHandler (MethodArgumentNotValidException.class ) public Map <String , Object > MethodArgumentNotValidExceptionHandler (MethodArgumentNotValidException e ) { Map<String, Object> body = new LinkedHashMap<>(); body.put("timestamp" , LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ))); List<String> errors = e.getBindingResult() .getFieldErrors() .stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.toList()); body.put("errors" , errors); return body; } }
PostMan测试,响应数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 请求数据 { "id" : 1 , "account" : "12345678" , "password" : "12312123" , "email" : "15096021m" } # 全局异常处理后的响应数据 { "timestamp" : "2020-09-19 14:07:05" , "errors" : [ "邮箱格式不正确" ] }
表单传参校验 使用表单传参,即不使用@RequestBody
,跟上面的第一类异常捕获的异常类型不同而已。
1 2 3 4 5 6 7 8 9 10 11 @Controller public class LoginController { @PostMapping ("/test1" ) @ResponseBody public AjaxResult test1 (@Validated User user) { System.out.println(user); return user; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler (MethodArgumentNotValidException.class ) public ResultVO <Map <String , Object >> MethodArgumentNotValidExceptionHandler (MethodArgumentNotValidException e ) { Map<String, Object> body = new LinkedHashMap<>(); body.put("timestamp" , LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ))); List<String> errors = e.getBindingResult() .getFieldErrors() .stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.toList()); body.put("errors" , errors); return new ResultVO<>(ResultCode.FAILED,body); } }
单个参数校验 直接在参数前加上校验注解
1 2 3 4 5 6 7 8 9 10 @RestController @Validated public class UserController { @GetMapping ("/test3" ) public AjaxResult test3 (@NotNull(message = "name不能为空" ) String name ,@Email (message ="邮箱格式不正确" ) String email) { System.out.println(name); System.out.println(email); return AjaxResult.success(name+" " +email); } }
注意:需要在类上添加@Validated注解,否则不会校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler (value = ValidationException.class ) public AjaxResult handleBindException (ValidationException ex ) { AjaxResult body = new AjaxResult(); body.put("timestamp" , new Date()); List<String> errors = new LinkedList<>(); if (ex instanceof ConstraintViolationException) { ConstraintViolationException exs = (ConstraintViolationException) ex; Set<ConstraintViolation<?>> violations = exs.getConstraintViolations(); for (ConstraintViolation<?> item : violations) { errors.add(item.getMessage()); } } body.put("errors" , errors); body.put("code" ,900 ); body.put("msg" ,"提交的数据校验失败" ); return body; } }