Spring Boot 入参校验及全局异常处理

版本依赖

  • JDK 17
  • Spring Boot 3.2.0

源码地址:Gitee

Spring Boot validation

spring-boot-starter-validation是基于hibernate-validator的实现,在Spring Boot项目中直接导入spring-boot-starter-validation即可。

@Valid 和 @Validated 的区别

  1. 适用范围:
    • @Valid 是 Java 校验(JSR-303)的一部分,通常用于标注在方法参数或方法返回值上,以触发参数校验或返回结果的校验。
    • @Validated 是 Spring 提供的,用于在方法级别进行校验。它支持分组校验,并且可以标注在类或方法上。
  2. 分组校验:
    • @Valid 支持分组校验,可以通过定义校验接口的不同分组来控制不同情况下的校验规则。
    • @Validated 也支持分组校验,但是它的分组校验是通过在校验注解上指定分组来实现的。
  3. 校验方式:
    • @Valid 主要用于标注在方法参数或返回值上,触发 Bean Validation 校验。
    • @Validated 主要用于方法级别的校验,并且支持 Spring 提供的校验方式,例如 Spring 的 @NotEmpty@Range 等。
  4. 引入依赖:
    • 使用 @Valid 需要引入 Java Bean Validation 的相关依赖,比如 Hibernate Validator。
    • 使用 @Validated 需要引入 Spring 的相关依赖,它是 Spring 提供的一个校验框架。
  5. 支持的校验注解:
    • @Valid 支持 JSR-303(Bean Validation)提供的注解,比如 @NotNull@Size 等。
    • @Validated 支持 Spring 提供的校验注解,例如 @NotEmpty@Range@Email 等。
  6. 参数校验异常类
    • @Valid 异常类为 org.springframework.web.bind.MethodArgumentNotValidException
    • Validated 异常类为 jakarta.validation.ConstraintViolationException

参数校验常用注解

@Null验证对象是否为null
@NotNull验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty检查约束元素是否为NULL或者是EMPTY.
@AssertTrue验证 Boolean 对象是否为 true
@AssertFalse验证 Boolean 对象是否为 false
@Size(min=, max=)验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=)验证注解的元素值长度在min和max区间内
@Past验证 Date 和 Calendar 对象是否在当前时间之前
@Future验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern验证 String 对象是否符合正则表达式的规则
@Min验证 Number 和 String 对象是否大等于指定的值
@Max验证 Number 和 String 对象是否小等于指定的值
@DecimalMax被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=)验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=)验证注解的元素值在最小值和最大值之间

用例代码

导入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>
定义对象参数
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.hibernate.validator.constraints.Length;import java.io.Serial;
import java.io.Serializable;@Data
public class RequestVO implements Serializable {@Serialprivate static final long serialVersionUID = 1L;@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "手机号码不能为空")@Length(min = 11, max = 11, message = "手机号码格式错误")private String phoneNumber;
}
定义测试Controller
package com.yiyan.study.controller;import com.yiyan.study.model.RequestVO;
import com.yiyan.study.model.Result;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
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;/*** 参数校验及全局异常处理测试Controller*/
@RestController
@RequestMapping("/ex")
@Validated
public class ExController {@GetMapping("/param_ex")public Result<RequestVO> paramEx(@Valid RequestVO request) {return Result.success();}@GetMapping("/param_in_query")public Result<String> paramInQuery(@NotBlank(message = "PARAM 不能为空") String param,@Min(value = 1, message = "number不能小于1") Integer number) {return Result.success();}
}

Spring Boot 全局异常处理

参数注释

  • @RestControllerAdvice: 能够捕获应用中所有控制器抛出的异常
  • @ExceptionHandler:方法中定义统一的返回格式,比如将异常信息封装成一个标准的 JSON 对象,并设置响应状态码等。
  • @ResponseStatus:定义响应的HttpStatus,如400,401,403等

自定义业务异常类

import lombok.Data;
import lombok.EqualsAndHashCode;import java.io.Serial;
import java.io.Serializable;/*** 自定义业务异常*/
@EqualsAndHashCode(callSuper = true)
@Data
public class BizException extends RuntimeException implements Serializable {@Serialprivate static final long serialVersionUID = 1L;/*** 错误码*/private final String errorCode;/*** 错误信息*/private final String errorMessage;public BizException(String errorCode, String errorMessage) {super(errorCode);this.errorCode = errorCode;this.errorMessage = errorMessage;}public BizException(String errorCode, String errorMessage, Throwable cause) {super(errorCode, cause);this.errorCode = errorCode;this.errorMessage = errorMessage;}@Overridepublic synchronized Throwable fillInStackTrace() {return this;}
}

自定义API统一返回结构

示例

package com.yiyan.study.model;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;import java.io.Serial;
import java.io.Serializable;/*** 接口统一返回*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Result<T> implements Serializable {@Serialprivate static final long serialVersionUID = 1L;/*** 请求Status*/private String code;/*** 业务信息*/private String message;/*** 返回数据*/private T data;/*** Instantiates a new Result.*/public Result() {}public Result(String code, String message, T data) {this.code = code;this.message = message;this.data = data;}public static <T> Result<T> success() {return new Result<>("200", "请求成功", null);}public static <T> Result<T> success(String code, String message, T data) {return new Result<>(code, message, data);}public static <T> Result<T> error(String code, String message, T data) {return new Result<>(code, message, data);}public static <T> Result<T> error(String code, String message) {return error(code, message, null);}}

自定义全局异常处理类

import com.yiyan.study.model.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.List;
import java.util.Map;
import java.util.TreeMap;/*** 全局异常处理*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {/*** 处理自定义的业务异常** @param req the req* @param e   the e* @return result*/@ExceptionHandler(value = BizException.class)public Result<BizException> bizExceptionHandler(HttpServletRequest req, BizException e) {log.error("[ {} ] {} 请求异常: {}", req.getMethod(), req.getRequestURL(), e.getErrorCode());return Result.error(e.getErrorCode(), e.getErrorMessage());}/*** 参数异常信息返回** @param req the req* @param e   the e* @return result*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result<Map<String, String>> methodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException e) {List<ObjectError> allErrors = e.getBindingResult().getAllErrors();log.error("[ {} ] {} 请求参数校验错误", req.getMethod(), req.getRequestURL());Map<String, String> paramExceptionInfo = new TreeMap<>();for (ObjectError objectError : allErrors) {FieldError fieldError = (FieldError) objectError;log.error("参数 {} = {} 校验错误:{}", fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());paramExceptionInfo.put(fieldError.getField(), fieldError.getDefaultMessage());}return Result.error(HttpStatus.BAD_REQUEST.toString(), "PARAM_EXCEPTION", paramExceptionInfo);}/*** 参数异常信息返回** @param req the req* @param e   the e* @return result*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = ConstraintViolationException.class)public Result<String> constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException e) {log.error("[ {} ] {} 请求参数校验错误", req.getMethod(), req.getRequestURL());return Result.error(HttpStatus.BAD_REQUEST.toString(), "PARAM_EXCEPTION", e.getMessage());}/*** 处理其他异常** @param req the req* @param e   the e* @return result*/@ExceptionHandler(value = Exception.class)public Result<String> exceptionHandler(HttpServletRequest req, Exception e) {log.error("[ {} ] {} 未定义异常: {}", req.getMethod(), req.getRequestURL(), e.getMessage());return Result.error("500", e.getMessage());}
}

测试

springboot3-全局异常处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/581853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《对话品牌》——活到老“养”到老

本期节目《对话品牌》栏目组邀请到了深圳壹常青健康管理有限公司董事长邬锡娣女士参加栏目录制&#xff0c;分享其企业故事&#xff0c;树立品牌形象&#xff0c;提升品牌价值&#xff01; 节目嘉宾&#xff1a;邬锡娣女士 节目主持人&#xff1a;董倩 节目播出平台&#xf…

在线教育系统源码解读:定制化企业培训APP的开发策略

当下&#xff0c;企业培训正经历着一场数字化的迭代&#xff0c;定制化企业培训APP应运而生&#xff0c;成为提升员工技能、推动企业发展的重要工具。下文小编将与大家一同深入了解在线教育系统的源码&#xff0c;探讨开发定制化企业培训APP的策略&#xff0c;以满足不同企业的…

C#获取windows系统资源使用情况

1.前言 之前有一篇博客介绍如何获取Linux服务器上的资源使用情况《Java 获取服务器资源&#xff08;内存、负载、磁盘容量&#xff09;》&#xff0c;这里介绍如何通过C#获取Window系统的资源使用。 2.获取服务器资源 2.1.内存 [DllImport("kernel32.dll")][retu…

jenkins解决工具找不到的问题

--------------------------插件选择版本最好能跟服务器对上

香橙派5plus从ssd启动Ubuntu

官方接口图 我实际会用到的就几个接口&#xff0c;背面的话就一个M.2固态的位置&#xff1a; 其中WIFI模块的接口应该也可以插2230的固态&#xff0c;不过是pcie2.0的速度&#xff0c;背面的接口则是pcie3.0*4的速度&#xff0c;差距还是挺大的。 开始安装系统 准备工作 一张…

C语言中关于switch语句的理解

首先我们来看一下switch的定义 switch&#xff08;整型表达式&#xff09; { case 整型常量表达式: 语句&#xff1b; } 我们在书写时要注意一下&#xff0c;无论是在switch还是case&#xff0c;后面跟着的都一定要是整型&#xff0c;而且case这一行写完时&#xff0c;最后要用…

图片放大后变模糊了怎么办?这个方法惊艳你

我们需要了解为什么图片放大会模糊。在照片放大时&#xff0c;像素也会随之增加。如果图片的像素不足&#xff0c;那么放大后每个像素的大小也会增加&#xff0c;从而导致细节模糊。 那么&#xff0c;面对这个问题&#xff0c;我们该如何解决呢&#xff1f;别急&#xff0c;让…

狗笼,预计2028年将以 6.2%的复合年增长率增长

对于想要为爱犬提供安全舒适空间的宠物主人来说&#xff0c;狗笼是必不可少的宠物配件。由于宠物主人的数量不断增加以及人们对宠物安全和福祉的意识不断增强&#xff0c;狗笼市场一直在稳步增长。 全球市场分析&#xff1a;全球狗笼市场预计从 2021 年到 2028 年将以 6.2% 的复…

axios配置请求头content-type 和 get/post请求方式

axios配置请求头content-type https://blog.csdn.net/wojiushiwo945you/article/details/107653962 axios 是Ajax的一个插件&#xff0c;axios虽然是一个插件&#xff0c;但是我们不需要通过Vue.use(axios)来使用&#xff0c;下载完成后&#xff0c;只需在项目中引入即可。(一…

NFC刷卡soc芯片SI3262集成刷卡+触摸+ACD超低功耗一体

简介 13.56mhz刷卡soc芯片SI3262集成刷卡触摸ACD超低功耗&#xff0c;ACD模式刷卡距离可达到5cm以上&#xff0c;非常适用于小体积门锁&#xff0c;密码锁&#xff0c;柜锁&#xff0c;接下来介绍一下这款芯片的具体功能。 优势 1.超低功耗&#xff0c;最低功耗达 1.7uA&…

揭秘跨境电商ERP源码定制化需求及最佳实践

跨境电商ERP源码的定制化需求是跨境电商企业在整个ERP系统开发实施过程中需要重点关注的问题之一。本文将围绕跨境电商ERP源码定制化的需求和最佳实践展开深入探讨&#xff0c;为行业内的从业者和相关人士提供一些建议和思路。 定制化需求 跨境电商ERP的业务特点决定了对源码…

8.小明和完美序列

题目 import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();sc.nextLine();Map<Integer,Intege…

Flask登陆后登陆状态及密码的修改和处理

web/templates/common 是统一布局 登录成功 后flask框架服务器默认由login.html进入仪表盘页面index.html(/),该页面的设置在 (web/controllers/user/index.py)&#xff0c;如果想在 该仪表盘页面 将 用户信息 展示出来&#xff0c;就得想办法先获取到 当前用户的 登陆状态。…

2022年全国职业院校技能大赛高职组云计算正式赛卷第三场-公有云

2022 年全国职业院校技能大赛高职组云计算赛项试卷 【赛程名称】云计算赛项第三场-公有云 目录 2022 年全国职业院校技能大赛高职组云计算赛项试卷 【赛程名称】云计算赛项第三场-公有云 【任务 1】公有云服务搭建[10 分] 【任务 2】公有云服务运维[10 分] 【任务 3】公有云运维…

03.MySQL的体系架构

MySQL的体系架构 一、MySQL简介二、MySQL的体系架构三、MySQL的内存结构四、MySQL的文件结构 一、MySQL简介 MySQL是一个开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;由瑞典MySQL AB公司开发&#xff0c;后被Sun公司收购&#xff0c;Sun公司被Oracle…

文件操作安全之-目录穿越流量告警运营分析篇

本文从目录穿越的定义,目录穿越的多种编码流量数据包示例,目录穿越的suricata规则,目录穿越的告警分析研判,目录穿越的处置建议等几个方面阐述如何通过IDS/NDR,态势感知等流量平台的目录穿越类型的告警的线索,开展日常安全运营工作,从而挖掘有意义的安全事件。 目录穿越…

鸿蒙Harmony(八)ArkUI--状态管理器之@State

状态管理 在声明式UI中&#xff0c;是以状态驱动视图更新 状态&#xff1a;指驱动视图更新的数据&#xff08;被装饰器标记的变量&#xff09; StateProp 和 LinkProvide和 Consume State State装饰器标记的变量必须初始化&#xff0c;不能为空值State支持Object 、class、…

【PostgreSQL内核学习(二十)—— 数据库中的遗传算法】

数据库中的遗传算法 概述个体的编码方式及种群初始化geqo 函数 适应值geqo_eval 函数gimme_tree 函数 父体选择策略geqo_selection 函数 杂交算子边重组杂交 ERX ( edge recombination crossover)gimme_edge_table 函数gimme_tour 函数 变异算子geqo_mutation 函数 声明&#x…

Java EE 网络原理之HTTP 响应详解

文章目录 1. 认识"状态码"(status code)2. 通过 form 表单构造 HTTP 请求3. 通过 ajax 构造 HTTP 请求 1. 认识"状态码"(status code) 表示了这次请求对应的响应&#xff0c;是什么样的状态 &#xff08;成功&#xff0c;失败&#xff0c;其他的情况&…

12.Go 函数

1、函数的声明 语法&#xff1a;func 函数名(参数列表)(返回参数列表){函数体} 函数名遵循标识符的命名规则&#xff0c;首字母的大小写决定该函数在其他包的可见性&#xff1a;大写时其他包可见&#xff0c;小写时只有相同的包可以访问&#xff1b;函数的参数和返回值需要使…