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

2023-12-28 17:52

本文主要是介绍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-全局异常处理

这篇关于Spring Boot 入参校验及全局异常处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/546772

相关文章

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1