当前位置: 首页 > news >正文

《学会 SpringBoot · 参数校验》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • 参数校验
      • 使用步骤
      • 验证效果
      • 全局异常
      • 常用校验
      • 关于 @RequestBody
    • 总结陈词

CSDN.gif

写在前面的话

此篇博文介绍一下 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, "请求参数验证失败");}}
}

修改后,测试效果如下:
image.png


常用校验

@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 没效果。
如果是实体入参,又想要校验,可以实现自定义参数绑定器,将请求参数映射到实体类,这个后面再补充。

总结陈词

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • MyBatis基本工作原理
  • vue3中父子组件的双向绑定defineModel详细使用方法
  • Redis(三)
  • 27、美国国家冰雪中心(NSIDC)海冰密集度月数据下载与处理
  • MFC窗口大小最大化最小化随拖动调整大小
  • Golang | Leetcode Golang题解之第283题移动零
  • 【Go系列】Go的UI框架Fyne
  • SQL Server中非结构化数据的存储神器:文件表的魔力
  • 21 B端产品经理之技术常识(1)
  • Python | Leetcode Python题解之第284题窥视迭代器
  • Alternating Sum
  • web基础,http协议,apache概念及nginx
  • C#小结:string、double、TimeSpan等常见类型的小结和坑点
  • mysql的存储过程:
  • go操作aws s3
  • 收藏网友的 源程序下载网
  • 【mysql】环境安装、服务启动、密码设置
  • Java Agent 学习笔记
  • JS基础之数据类型、对象、原型、原型链、继承
  • MQ框架的比较
  • mysql innodb 索引使用指南
  • MySQL数据库运维之数据恢复
  • node和express搭建代理服务器(源码)
  • npx命令介绍
  • PHP变量
  • scrapy学习之路4(itemloder的使用)
  • Spark学习笔记之相关记录
  • Spring Boot快速入门(一):Hello Spring Boot
  • ViewService——一种保证客户端与服务端同步的方法
  • vue.js框架原理浅析
  • windows-nginx-https-本地配置
  • 飞驰在Mesos的涡轮引擎上
  • 检测对象或数组
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 前端js -- this指向总结。
  • 前端代码风格自动化系列(二)之Commitlint
  • 三分钟教你同步 Visual Studio Code 设置
  • elasticsearch-head插件安装
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • #100天计划# 2013年9月29日
  • #QT(TCP网络编程-服务端)
  • (CVPRW,2024)可学习的提示:遥感领域小样本语义分割
  • (第一天)包装对象、作用域、创建对象
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (篇九)MySQL常用内置函数
  • (一)Dubbo快速入门、介绍、使用
  • (转)程序员技术练级攻略
  • (转载)深入super,看Python如何解决钻石继承难题
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET BackgroundWorker
  • .net 流——流的类型体系简单介绍
  • .NET8 动态添加定时任务(CRON Expression, Whatever)
  • /etc/shadow字段详解
  • /var/log/cvslog 太大