SpringBoot 中使用 @Valid 注解 + Exception 全局处理器优雅处理参数验证

本文介绍了如何在 SpringBoot 中利用 @Valid 注解进行参数验证,并结合全局异常处理器提升代码的优雅性。通过在实体类中添加 @Valid 相关注解,接口方法中使用 @Valid,配合自定义的全局异常处理器,可以简洁地处理参数验证。文章还提供了具体的代码示例,包括 Maven 依赖、自定义异常类、响应枚举类、响应对象类等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

三、@Valid 的相关注解

四、使用 @Valid 进行参数效验步骤

  • 实体类中添加 @Valid 相关注解

  • 接口类中添加 @Valid 注解

  • 全局异常处理类中处理 @Valid 抛出的异常

五、SpringBoot 中使用 @Valid 示例

  • Maven 引入相关依赖

  • 自定义个异常类

  • 自定义响应枚举类

  • 自定义响应对象类

  • 自定义实体类中添加 @Valid 相关注解

  • Controller 中添加 @Valid 注解

  • 全局异常处理

  • 启动类

  • 示例测试

相关地址:

Spring Servlet 文档:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/web/servlet

示例项目 Github:https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-filter-example

系统环境:

Jdk 版本:jdk 8

SpringBoot 版本:2.2.1.RELEASE

一、为什么使用 @Valid 来验证参数


在平常通过 Spring 框架写代码时候,会经常写接口类,相信大家对该类的写法非常熟悉。在写接口时经常要写效验请求参数逻辑,这时候我们会常用做法是写大量的 if 与 if else 类似这样的代码来做判断,如下:

@RestController

public class TestController {

@PostMapping(“/user”)

public String addUserInfo(@RequestBody User user) {

if (user.getName() == null || “”.equals(user.getName()) {

} else if(user.getSex() == null || “”.equals(user.getSex())) {

} else if(user.getUsername() == null || “”.equals(user.getUsername())) {

} else {

}

}

}

这样的代码如果按正常代码逻辑来说,是没有什么问题的,不过按优雅来说,简直糟糕透了。不仅不优雅,而且如果存在大量的验证逻辑,这会使代码看起来乱糟糟,大大降低代码可读性,那么有没有更好的方法能够简化这个过程呢?

答案当然是有,推荐的是使用 @Valid 注解来帮助我们简化验证逻辑。

二、@Valid 注解的作用


注解 @Valid

<think>好的,我现在需要在Spring Boot中实现对手机号和邮箱的验证。首先,我应该了解Spring Boot的验证机制,可能涉及到Bean Validation API,比如Hibernate Validator。用户提到的最佳实践,可能需要自定义注解验证器。 首先,项目需要添加Spring Boot Validation的依赖。检查pom.xml或者build.gradle,确认是否有spring-boot-starter-validation。如果没有,需要添加进去。 接下来,处理手机号验证。标准的注解可能不够,比如@Pattern,但不同国家的手机号格式不同,可能需要自定义验证。比如,创建一个@Phone注解,并实现ConstraintValidator接口。在isValid方法中编写正则表达式,例如中国的手机号是1开头,11位数字。正则可能是^1[3-9]\\d{9}$。但用户可能需要更灵活的方式,比如可配置的国家代码,或者使用谷歌的libphonenumber库来解析和验证,这样更国际化。 然后是邮箱验证。Spring已经有@Email注解,但可能需要加强,比如检查域名是否存在,或者使用更严格的正则。或者同样自定义一个@Email注解,结合RFC 5322的标准正则表达式。 在实体类中,给对应的字段添加这些注解,比如: public class User { @Phone private String phone; @Email private String email; } 然后,在Controller的方法参数前加上@Validated,以触发验证处理验证错误的话,可以使用BindingResult来收集错误信息,或者使用全局异常处理,捕获MethodArgumentNotValidException,并返回统一的错误响应。 另外,需要考虑国际化,错误消息是否可以外部化,比如在messages.properties中定义。还有,可能需要将手机号和邮箱的验证逻辑抽象成公共组件,方便复用。 可能的问题:自定义注解的实现是否正确?正则表达式是否覆盖所有情况?是否需要服务端实时验证(比如发送短信验证码),这时候可能需要在Service层进行额外处理,而不仅仅是Bean Validation。 最后,测试验证是否生效,可以通过编写单元测试或者使用Postman发送请求,查看返回的错误信息是否符合预期。 现在,我需要将这些步骤整理成结构化的回答,确保符合用户的要求,比如代码示例使用正确的格式,引用相关文档。同时生成相关问题。</think>在Spring Boot中实现手机号和邮箱验证可通过Bean Validation结合自定义注解完成,以下是分步指南: ### 一、添加依赖 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> ``` ### 二、手机号验证实现 1. 创建自定义注解 ```java @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = PhoneValidator.class) public @interface Phone { String message() default "Invalid phone number"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ``` 2. 实现验证逻辑(示例中国手机号) ```java public class PhoneValidator implements ConstraintValidator<Phone, String> { private static final Pattern CHINA_PATTERN = Pattern.compile("^1[3-9]\\d{9}$"); @Override public boolean isValid(String value, ConstraintValidatorContext context) { return value != null && CHINA_PATTERN.matcher(value).matches(); } } ``` ### 三、邮箱验证实现 1. 增强型邮箱验证注解 ```java @Documented @Constraint(validatedBy = EnhancedEmailValidator.class) @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface EnhancedEmail { String message() default "Invalid email format"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ``` 2. 使用RFC 5322正则表达式 ```java public class EnhancedEmailValidator implements ConstraintValidator<EnhancedEmail, String> { private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"); @Override public boolean isValid(String value, ConstraintValidatorContext context) { return value != null && EMAIL_PATTERN.matcher(value).matches(); } } ``` ### 四、在实体类应用验证 ```java public class UserDTO { @Phone private String phone; @EnhancedEmail private String email; // getters/setters } ``` ### 五、控制器验证处理 ```java @PostMapping("/users") public ResponseEntity<?> createUser(@Valid @RequestBody UserDTO userDTO, BindingResult result) { if (result.hasErrors()) { return ResponseEntity.badRequest().body( result.getAllErrors().stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.toList()) ); } // 处理业务逻辑 return ResponseEntity.ok("Validation passed"); } ``` ### 六、国际化配置(可选) 在`messages.properties`中添加: ```properties Phone.userDTO.phone=手机号格式不正确 EnhancedEmail.userDTO.email=邮箱格式无效 ``` ### 七、进阶实践 1. 使用Google libphonenumber进行国际号码验证 ```java // 添加依赖 <dependency> <groupId>com.googlecode.libphonenumber</groupId> <artifactId>libphonenumber</artifactId> <version>8.13.5</version> </dependency> // 修改验证器 PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); try { PhoneNumber numberProto = phoneUtil.parse(value, "CN"); return phoneUtil.isValidNumber(numberProto); } catch (NumberParseException e) { return false; } ``` 2. 邮箱域名验证(需网络请求) ```java public static boolean verifyDomain(String email) { try { String domain = email.substring(email.indexOf('@') + 1); return InetAddress.getByName(domain).isReachable(2000); } catch (Exception e) { return false; } } ``` **最佳实践建议**: 1. 验证逻辑应同时包含前端和后端验证 2. 敏感信息验证建议结合短信/邮件验证码 3. 定期更新正则表达式匹配规则 4. 对验证错误进行日志监控 5. 使用AOP实现验证逻辑解耦 ```plantuml @startuml skinparam backgroundColor #EEEBDC entity "UserDTO" as user { +String phone +String email } entity "PhoneValidator" as pv { +isValid() } entity "EmailValidator" as ev { +isValid() } user -> pv : @Phone user -> ev : @EnhancedEmail pv --> PhoneNumberUtil: 使用libphonenumber ev --> DNS: 域名解析验证 @enduml ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值