@Validated或@Valid参数注解校验、自定义手机号注解检验及优雅统一异常处理

本文主要是介绍@Validated或@Valid参数注解校验、自定义手机号注解检验及优雅统一异常处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

@Validated或@Valid参数注解校验、自定义手机号注解检验及优雅统一异常处理

文章目录

  • 1.@Validated和@Valid的区别和使用注意事项
    • 1.1来源
    • 1.2注解位置
    • 1.3是否支持分组
    • 1.4是否支持嵌套校验
  • 2.集成依赖
  • 3.@Valid常用注解
  • 4.@Validated常用注解
  • 4.自定手机号注解校验
    • 4.1引入依赖
    • 4.2实现代码
  • 5.优雅统一异常处理
  • 6.总结

1.@Validated和@Valid的区别和使用注意事项

1.1来源

@Validated :是Spring框架特有的注解,属于Spring的一部分,也是JSR 303的一个变种。它提供了一些 @Valid 所没有的额外功能,比如分组验证。
@Valid:Java EE提供的标准注解,它是JSR 303规范的一部分,主要用于Hibernate Validation等场景。

1.2注解位置

@Validated : 用在类、方法和方法参数上,但不能用于成员属性。
@Valid:可以用在方法、构造函数、方法参数和成员属性上。

1.3是否支持分组

@Validated :支持分组验证,可以更细致地控制验证过程。此外,由于它是Spring专有的,因此可以更好地与Spring的其他功能(如Spring的依赖注入)集成。
@Valid:主要支持标准的Bean验证功能,不支持分组验证。 嵌套验证

1.4是否支持嵌套校验

@Validated :不支持嵌套验证。
@Valid:支持嵌套验证,可以嵌套验证对象内部的属性。

2.集成依赖

以下依赖推荐使用spring-boot-starter-validation,可以跟springBoot更好的集成

	   <!--第一种:valid依赖--><dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>版本号</version></dependency><!--第二种:集成于web依赖中(注意版本号)--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>xxxx.RELEASE</version></dependency><!-- 第三种:springboot的validation--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

3.@Valid常用注解

image-20240416113004067

4.@Validated常用注解

image-20240416113020765

4.自定手机号注解校验

4.1引入依赖

        <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.8</version></dependency>

4.2实现代码

@Phone注解类

package xxxxx.validator;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 指定约束处理器,也就是手机号格式验证是哪个类来做校验
@Constraint(validatedBy = {PhoneValidator.class})
public @interface Phone {//这里不采用正则表达式做手机号的格式配置//String pattern() default "^(?:(?:\\+|00)86)?1\\d{10}$";String message() default "手机号格式不正确";// groups用来指定分组,可以让校验采取不同的机制,当前默认未指定任何分组机制,默认每次都要进行校验Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}

PhoneValidator类

package xxx.validator;import cn.hutool.core.lang.Validator;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;/*** 校验处理器:做手机号码格式验证的核心类*/
public class PhoneValidator implements ConstraintValidator<Phone, String> {// 注解对象private Phone phone;// 初始化【Phone】对象@Overridepublic void initialize(Phone constraintAnnotation) {phone = constraintAnnotation;}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {//获取【Phone】对象的手机格式验证表达式//String pattern = phone.pattern();//Pattern compile = Pattern.compile(pattern);//Matcher matcher = compile.matcher(value);// return matcher.matches();if (Objects.isNull(value)) {throw new RuntimeException("手机号字段不为空");}boolean isMobile = Validator.isMobile(value);return isMobile;}}

  这里使用hutool工具包里面的Validator.isMobile(value)方法实现一个手机号注解校验,不用自己去写正则表达是,使用hutool的更全面优雅,也可以使用hutool工具包里面的Validator.isEmail(value)方法实现一个自定义注解校验邮箱的,因为@Validated和@Valid的依赖中有实现邮箱注解校验的注解了,所以我们不需要自己去写一个邮箱注解校验。

5.优雅统一异常处理

GlobalExceptionHandler类

package xxxxx.handler;import cn.dev33.satoken.exception.NotPermissionException;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import xxxxx.RestResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.time.DateTimeException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;/*** 全局异统一常处理**/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {private static final Integer STATUS_404 = 404;public static final String ERROR_MSG_404 = "接口地址不存在";@ExceptionHandler(Exception.class)public RestResponse exceptionHandler(Exception e) {if (e instanceof MissingServletRequestParameterException) {//URL请求参数缺失异常处理带?xxx1=xxx1 & xxx2=xxx2MissingServletRequestParameterException m1 = (MissingServletRequestParameterException) e;return RestResponse.fail("请求参数 " + m1.getParameterName() + " 不能为空");}if (e instanceof ConstraintViolationException) {//@Valid参数校验异常处理ConstraintViolationException e2 = (ConstraintViolationException) e;List<String> s = new ArrayList<>();Set<ConstraintViolation<?>> constraintViolations = e2.getConstraintViolations();for (ConstraintViolation<?> c : constraintViolations) {String message = c.getMessage();s.add(message);}if (CollectionUtil.isNotEmpty(s)) {return RestResponse.fail(s);}}if (e instanceof MethodArgumentNotValidException) {//@Validated参数校验异常处理//嵌套对象字段校验MethodArgumentNotValidException e3 = (MethodArgumentNotValidException) e;List<FieldError> fieldErrors = e3.getBindingResult().getFieldErrors();if (CollectionUtil.isNotEmpty(fieldErrors)) {Map<String, Object> validError = this.getValidError(fieldErrors);return RestResponse.fail(validError);}}if (e instanceof BindException) {//POST请求@RequestBody/@Validated参数绑定校验异常处理BindException e4 = (BindException) e;List<FieldError> fieldErrors = e4.getBindingResult().getFieldErrors();if (CollectionUtil.isNotEmpty(fieldErrors)) {Map<String, Object> validError = this.getValidError(fieldErrors);return RestResponse.fail(validError);}}if (e instanceof HttpRequestMethodNotSupportedException) {//捕获请求方法异常:比如post接口使用了getHttpRequestMethodNotSupportedException e5 = (HttpRequestMethodNotSupportedException) e;String method = e5.getMethod();return RestResponse.fail(method + "请求方法不被允许");}if (e instanceof HttpMessageNotReadableException) {HttpMessageNotReadableException e5 = (HttpMessageNotReadableException) e;Throwable rootCause = e5.getRootCause();//校验参数是否多余,导致不能识别if (rootCause instanceof JsonProcessingException) {JsonMappingException mappingException = (JsonMappingException) rootCause;String fieldName = mappingException.getPath().get(0).getFieldName();return RestResponse.fail("请求body中参数 " + fieldName + " 不能识别");}//校验日期字段转换是否异常if (rootCause instanceof DateTimeException) {return RestResponse.fail("请求body中日期转换异常");}return RestResponse.fail("请求body不为空");}if (e instanceof ValidationException) {ValidationException e6 = (ValidationException) e;Throwable cause = e6.getCause();if (Objects.nonNull(cause)) {return RestResponse.fail(cause.getMessage());}return RestResponse.fail("自定义字段校验注解处理异常");}//上篇文章的sa-token的异常if (e instanceof NotPermissionException) {NotPermissionException e7 = (NotPermissionException) e;log.error("NotPermissionException.msg:{}", e7.getMessage());return RestResponse.fail("没有此权限!");}//可以根据业务拓展业务异常//也可以处理其它异常return RestResponse.fail(e.getMessage());}/*** 获取校验错误信息*/private Map<String, Object> getValidError(List<FieldError> fieldErrors) {Map<String, Object> result = new HashMap<>(16);for (FieldError error : fieldErrors) {result.put(error.getField(), error.getDefaultMessage());}return result;}// 决定是否执行beforeBodyWrite()方法@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {return true;}@Overridepublic Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {if (o == null) {return RestResponse.fail();}//String类型需要特殊处理 手动转为json字符串if (o instanceof String) {return RestResponse.success(o);}if (o instanceof RestResponse) {return o;}//boolean类型 返回对应的成功或失败if (o instanceof Boolean) {return RestResponse.success(o);}//404时 返回特定信息 404也可以重写BasicErrorControllerif (is404(o)) {return RestResponse.fail(ERROR_MSG_404 + ":" + STATUS_404);}return o;}private boolean is404(Object o) {if (o instanceof Map) {Map<String, Object> map = Convert.toMap(String.class, Object.class, o);Integer status = Convert.toInt(map.get("status"));return STATUS_404.equals(status);}return false;}
}

6.总结

  使用@Validated或@Valid对controller接口的参数或controller中body的参数做检验可以让代码更整洁工整,不至于写很多if前置参数校验判断逻辑,在配和上优雅全局统一异常处理,使用本文的套路可以代码更优雅简洁工整清秀,代码可读性高和可维护性强,使开发人员更加专注于业务,我的分享到此结束了,希望对你有所启发和帮助,请一键三连,么么么哒!

这篇关于@Validated或@Valid参数注解校验、自定义手机号注解检验及优雅统一异常处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java进行文件格式校验的方案详解

《Java进行文件格式校验的方案详解》这篇文章主要为大家详细介绍了Java中进行文件格式校验的相关方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、背景异常现象原因排查用户的无心之过二、解决方案Magandroidic Number判断主流检测库对比Tika的使用区分zip

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

Python实现自动化接收与处理手机验证码

《Python实现自动化接收与处理手机验证码》在移动互联网时代,短信验证码已成为身份验证、账号注册等环节的重要安全手段,本文将介绍如何利用Python实现验证码的自动接收,识别与转发,需要的可以参考下... 目录引言一、准备工作1.1 硬件与软件需求1.2 环境配置二、核心功能实现2.1 短信监听与获取2.