概述
在开发过程中,对于前端传递过来的参数,一般都需要进行非空校验、长度校验、正则表达式校验等等,如果参数不符合规范,那么就需要返回错误信息。如果在每一个Controller方法中都写大量的这种if、else之类的判断,就会造成可读性极差的问题。为了解决这种情况,可以引入Hibernate校验对参数进行校验。Hibernate校验是通过在方法参数、实体类属性上添加特定注解来达到参数校验目标,若参数不符合要求则抛出异常
依赖坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.4.4</version>
</dependency>
示例
1.)校验普通参数
前提事项
.在被校验参数前添加Hibernate校验注解
.在被校验参数所属方法的所属类上添加注解@Validated来开启校验
注意事项
.由于@Validated是Spring提供的,所以有一定的局限性
示例
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
import jakarta.validation.constraints.NotBlank;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 登录控制类
*/
@Validated
@RestController
@RequestMapping("/security/login")
public class LoginController2 {
/**
* 登录
*/
@GetMapping("/login")
public void login(@NotBlank(message = "账号不能为空") String username, @NotBlank(message = "密码不能为空") String password) {
System.out.println(username);
System.out.println(password);
}
}
错误信息
2025-05-08 17:34:38.086 [ ] ERROR [ http-nio-9200-exec-4] [o.a.c.c.C.[.[.[/].[dispatcherServlet] ] => Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: jakarta.validation.ConstraintViolationException: login.password: 密码不能为空, login.username: 账号不能为空] with root cause
jakarta.validation.ConstraintViolationException: login.password: 密码不能为空, login.username: 账号不能为空
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:170)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)
at wxw.mengyuan.tool.requestlog.RequestLogAspect.requestLogPrint(RequestLogAspect.java:67)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:642)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:632)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:71)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727)
at wxw.mengyuan.bookkeeping.boot.security.login.controller.LoginController2$$SpringCGLIB$$0.login(<generated>)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at wxw.mengyuan.tool.blackwhitelist.BlackWhiteListFilter.doFilter(BlackWhiteListFilter.java:60)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at wxw.mengyuan.tool.xss.XSSFilter.doFilter(XSSFilter.java:15)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:1583)
2.)校验对象参数
前提事项
.被校验参数都为对象,需要在对象属性上添加Hibernate校验注解
.还需要在被校验参数前添加注解@Validated来开启校验
注意事项
.由于@Validated是Spring提供的,所以有一定的局限性
@Validated可以换成@Valid,这个是Java提供的,但是该注解仅能用于对象参数校验、且无法提供群组方式校验
示例:实体类
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class UserInfo {
@NotBlank(message = "账号不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
示例:控制类
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 堆代码 duidaima.com
* 登录控制类
*/
@RestController
@RequestMapping("/security/login")
public class LoginController2 {
/**
* 登录
*/
@PostMapping("/login")
public void login(@Validated @RequestBody UserInfo userInfo) {
System.out.println(userInfo);
}
}
错误信息
2025-05-09 09:00:47.825 [1920645089007394816] WARN [ http-nio-9200-exec-1] [o.s.w.s.m.s.DefaultHandlerExceptionResolver] => Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void wxw.mengyuan.bookkeeping.boot.security.login.controller.LoginController2.login(wxw.mengyuan.bookkeeping.boot.security.login.controller.UserInfo) with 2 errors: [Field error in object 'userInfo' on field 'username': rejected value [null]; codes [NotBlank.userInfo.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.username,username]; arguments []; default message [username]]; default message [账号不能为空]] [Field error in object 'userInfo' on field 'password': rejected value [null]; codes [NotBlank.userInfo.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.password,password]; arguments []; default message [password]]; default message [密码不能为空]] ]
3.)群组方式校验
前提事项
.创建一个接口,每一个接口代表一个群组
.被校验参数都为对象,需要在对象属性上添加Hibernate校验注解,并设置在哪些群组中生效
.还需要在被校验参数前添加注解@Validated来开启校验
注意事项
.由于@Validated是Spring提供的,所以有一定的局限性
示例:接口一
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
/**
* 账号登录群组
*/
public interface AccountLoginGroup {
}
示例:接口二
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
/**
* 手机号登录群组
*/
public interface PhoneLoginGroup {
}
示例:实体类
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class UserInfo {
@NotBlank(message = "账号不能为空", groups = { AccountLoginGroup.class })
private String username;
@NotBlank(message = "密码不能为空", groups = { AccountLoginGroup.class, PhoneLoginGroup.class })
private String password;
@NotBlank(message = "手机号不能为空", groups = { PhoneLoginGroup.class })
private String phone;
}
示例:控制类
package wxw.mengyuan.bookkeeping.boot.security.login.controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 登录控制类
*/
@RestController
@RequestMapping("/security/login")
public class LoginController2 {
/**
* 登录
*/
@PostMapping("/login")
public void login(@Validated({ AccountLoginGroup.class }) @RequestBody UserInfo userInfo) {
System.out.println(userInfo);
}
}
错误信息
2025-05-09 09:21:07.270 [1920650204097105920] WARN [ http-nio-9200-exec-2] [o.s.w.s.m.s.DefaultHandlerExceptionResolver] => Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void wxw.mengyuan.bookkeeping.boot.security.login.controller.LoginController2.login(wxw.mengyuan.bookkeeping.boot.security.login.controller.UserInfo) with 2 errors: [Field error in object 'userInfo' on field 'username': rejected value [null]; codes [NotBlank.userInfo.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.username,username]; arguments []; default message [username]]; default message [账号不能为空]] [Field error in object 'userInfo' on field 'password': rejected value [null]; codes [NotBlank.userInfo.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.password,password]; arguments []; default message [password]]; default message [密码不能为空]] ]
小提示
1.所有校验注解,若没有设置群组,则默认为Default.class群组
2.在@Validated中指定了校验群组时,只有被指定群组生效、其他群组不生效,Default.class群组也遵循该规则