📢 大家好,我是 【战神刘玉栋】,有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 没效果。
 如果是实体入参,又想要校验,可以实现自定义参数绑定器,将请求参数映射到实体类,这个后面再补充。
总结陈词
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。




















