📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
文章目录
- 写在前面的话
- 参数校验
- 使用步骤
- 验证效果
- 全局异常
- 常用校验
- 关于 @RequestBody
- 总结陈词
写在前面的话
此篇博文介绍一下 SpringBoot 中的参数校验基本用法。
SpringBoot 版本:3.3.2
参数校验
使用步骤
Step1、引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
注意:SpringBoot 2.3 以后默认 spring-boot-starter-web 组件不包含该依赖需要单独引入。
Step2、实体添加注解
public class ZyTeacherInfo {@Schema(description = "教师编号")@NotBlank(message = "教师编号不能为空")private java.lang.String teaCode;@Schema(description = "教师名称")@Size(min = 2, max = 8, message = "教师名称长度需在2-8位")private java.lang.String teaName;
}
Step3、接口参数添加 @Validated
@Operation(summary = "更新教师信息表JSON")
@PostMapping("/updateJson")
public void updateJson(@RequestBody @Validated ZyTeacherInfo zyTeacherInfo) {zyTeacherInfo.setModifiedTime(new Date());zyTeacherInfoService.update(zyTeacherInfo);
}
Tips:推荐用@Validated注解,因为它能够支持 Spring 提供的校验注解,并且具有更好的集成性,@Valid注解是 Java 标准库提供的,用于在任何地方触发参数校验。
验证效果
启动服务,测试一下不传入参的清空,接口提示如下:
可以看到参数未通过返回的信息很不友好,我们需要通过全局异常捕获来处理一下返回友好的提示信息。
{"timestamp": "2024-07-29T05:54:49.411+00:00","status": 400,"error": "Bad Request","trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.lw.sbdemo2.web.ZyTeacherInfoController.updateJson(com.lw.sbdemo2.entity.ZyTeacherInfo) with 2 errors: [Field error in object 'zyTeacherInfo' on field 'teaName': rejected value []; codes [Size.zyTeacherInfo.teaName,Size.teaName,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [zyTeacherInfo.teaName,teaName]; arguments []; default message [teaName],8,2]; default message [教师名称长度需在2-8位]] [Field error in object 'zyTeacherInfo' on field 'teaCode': rejected value []; codes [NotBlank.zyTeacherInfo.teaCode,NotBlank.teaCode,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [zyTeacherInfo.teaCode,teaCode]; arguments []; default message [teaCode]]; default message [教师编号不能为空]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:144)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:224)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:178)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)\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:1089)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)\r\n\tat com.github.xiaoymin.knife4j.extend.filter.basic.JakartaServletSecurityBasicAuthFilter.doFilter(JakartaServletSecurityBasicAuthFilter.java:55)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:904)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)\r\n\tat java.base/java.lang.Thread.run(Thread.java:842)\r\n","message": "Validation failed for object='zyTeacherInfo'. Error count: 2","errors": [{"codes": ["Size.zyTeacherInfo.teaName","Size.teaName","Size.java.lang.String","Size"],"arguments": [{"codes": ["zyTeacherInfo.teaName","teaName"],"arguments": null,"defaultMessage": "teaName","code": "teaName"},8,2],"defaultMessage": "教师名称长度需在2-8位","objectName": "zyTeacherInfo","field": "teaName","rejectedValue": "","bindingFailure": false,"code": "Size"},{"codes": ["NotBlank.zyTeacherInfo.teaCode","NotBlank.teaCode","NotBlank.java.lang.String","NotBlank"],"arguments": [{"codes": ["zyTeacherInfo.teaCode","teaCode"],"arguments": null,"defaultMessage": "teaCode","code": "teaCode"}],"defaultMessage": "教师编号不能为空","objectName": "zyTeacherInfo","field": "teaCode","rejectedValue": "","bindingFailure": false,"code": "NotBlank"}],"path": "/zyTeacherInfo/updateJson"
}
全局异常
之前的博文《框架封装 · 统一异常处理和返回值包装》,介绍了 SpringBoot 框架的统一异常和返回值包装的处理过程,这里就可以使用到。
参考代码如下所示,可以整体捕捉 Throwable 异常,内部处理,也可以单独捕捉 MethodArgumentNotValidException 异常,单独处理。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(value = Throwable.class)public ResultModel jsonErrorHandler(HttpServletRequest req, Throwable e) throws Exception {log.error("请求发生异常,URL:{},HTTP_METHOD:{},IP:{},错误信息:{}", req.getRequestURL().toString(), req.getMethod(), req.getRemoteAddr(), e.getMessage());ResultModel resultModel;if (e instanceof ApiException) {resultModel = ResultModel.fail(((ApiException) e).getCode().getCode(), ((ApiException) e).getErrorMessage(), ((ApiException) e).getCode().getMessage());} else if (e instanceof NoHandlerFoundException) {resultModel = ResultModel.fail(ResponseCodeEnum.EX_PAGE_404, e.getMessage());} else if (e instanceof BindException) {List<String> errorInformation = ((BindException) e).getBindingResult().getAllErrors().stream().map(error -> Optional.ofNullable(error.getDefaultMessage()).orElse("default message")).toList();resultModel = ResultModel.fail(ResultModel.ERROR_CODE, null, errorInformation.get(0));} else {resultModel = ResultModel.fail(ResultModel.ERROR_CODE, null, e.getMessage());}return resultModel;}@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResultModel handleValidationExceptions(Exception ex) {log.error(ex.getMessage());// 从异常中获取字段错误信息FieldError fieldError = ((MethodArgumentNotValidException) ex).getBindingResult().getFieldError();if (fieldError != null) {// 获取错误提示信息String errorMessage = fieldError.getDefaultMessage();log.error(errorMessage);return ResultModel.fail(ResultModel.ERROR_CODE, null, errorMessage);} else {// 如果没有字段错误,返回默认错误信息log.error(ex.getMessage());return ResultModel.fail(ResultModel.ERROR_CODE, null, "请求参数验证失败");}}
}
修改后,测试效果如下:
常用校验
@NotNull:用于标记字段或方法参数不能为空。非null
@NotEmpty:用于标记集合、数组、字符串不能为空。非空集合、数组、字符串
@NotBlank:用于标记字符串不能为空且长度必须大于0。非null且非空字符串
@Size:用于标记集合、数组、字符串长度必须在指定范围内
@Min:用于标记数字类型的最小值
@Max:用于标记数字类型的最大值
@Email:用于标记字符串必须为邮箱格式
@NotNull 用于一般的非空校验,@NotEmpty用于集合、数组、字符串的非空校验,@NotBlank则用于字符串的非空校验且长度必须大于0。
关于 @RequestBody
@Validated,主要用于对复杂对象(如实体类)进行校验,验证其属性是否符合约束条件。
如果前面例子调整一下,去掉 @RequestBody,然后使用x-www-form-urlencoded
方式请求,是否可行。
@Operation(summary = "更新教师信息表JSON")
@PostMapping("/updateJson")
public void updateJson(@Validated ZyTeacherInfo zyTeacherInfo) {zyTeacherInfo.setModifiedTime(new Date());zyTeacherInfoService.update(zyTeacherInfo);
}
结果很明显,参数正常传递,@Validated 没效果。
如果是实体入参,又想要校验,可以实现自定义参数绑定器,将请求参数映射到实体类,这个后面再补充。
总结陈词
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。