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

Spring Validation校验

使用 JSR 303 (Bean Validation) 校验接口参数

JSR 303,也称为Bean Validation规范,提供了一种在Java应用程序中执行验证的标准化方式。它允许你通过注解直接在领域或者DTO(数据传输对象)类上定义校验规则。

1. 添加依赖

首先需要在项目中添加相关依赖:

<!-- Spring Boot 项目只需添加这个 starter -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency><!-- 非 Spring Boot 项目需要添加这些 -->
<dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version>
</dependency>
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>6.0.13.Final</version>
</dependency>

2. 在实体类上添加校验注解

import javax.validation.constraints.*;public class UserDTO {@NotNull(message = "用户ID不能为空")private Long id;@NotBlank(message = "用户名不能为空")@Size(min = 2, max = 20, message = "用户名长度必须在2-20个字符之间")private String username;@Min(value = 18, message = "年龄必须大于等于18岁")@Max(value = 120, message = "年龄必须小于等于120岁")private Integer age;@Email(message = "邮箱格式不正确")private String email;@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "密码必须包含大小写字母和数字,且长度至少8位")private String password;// getters and setters
}

3. 在 Controller 中使用校验

3.1 校验请求体

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/users")
public class UserController {@PostMappingpublic ResponseEntity<String> createUser(@RequestBody @Validated UserDTO userDTO) {// 如果校验失败,会抛出 MethodArgumentNotValidException// 业务逻辑处理return ResponseEntity.ok("用户创建成功");}
}

3.2 校验路径变量和请求参数

@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable @Min(1) Long id,@RequestParam @NotBlank String type) {// 业务逻辑return ResponseEntity.ok(userDTO);
}

4. 全局异常处理

import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {Map<String, String> errors = new HashMap<>();ex.getBindingResult().getAllErrors().forEach(error -> {String fieldName = ((FieldError) error).getField();String errorMessage = error.getDefaultMessage();errors.put(fieldName, errorMessage);});return ResponseEntity.badRequest().body(errors);}
}

5. 常用校验注解

注解说明
@NotNull值不能为null
@NotEmpty字符串/集合不能为null或空
@NotBlank字符串不能为null且必须包含至少一个非空白字符
@Size字符串/集合/数组的大小必须在指定范围内
@Min数字最小值
@Max数字最大值
@DecimalMin小数值最小值
@DecimalMax小数值最大值
@Digits数字的整数和小数部分的位数限制
@Past日期必须在过去
@PastOrPresent日期必须在过去或现在
@Future日期必须在未来
@FutureOrPresent日期必须在未来或现在
@Pattern字符串必须匹配正则表达式
@Email字符串必须是有效的电子邮件地址
@Positive数字必须是正数
@PositiveOrZero数字必须是正数或零
@Negative数字必须是负数
@NegativeOrZero数字必须是负数或零

6. 分组校验

可以定义不同的校验组,在不同场景下应用不同的校验规则:

public interface CreateGroup {}
public interface UpdateGroup {}public class UserDTO {@Null(groups = CreateGroup.class, message = "创建时ID必须为空")@NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")private Long id;// 其他字段...
}@PostMapping
public ResponseEntity<String> createUser(@RequestBody @Validated(CreateGroup.class) UserDTO userDTO) {// 业务逻辑
}

7. 自定义校验注解

当内置注解不能满足需求时,可以自定义校验注解:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;// 自定义注解
@Documented
@Constraint(validatedBy = PhoneNumberValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPhoneNumber {String message() default "无效的手机号码";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}// 自定义校验规则
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {@Overridepublic boolean isValid(String phoneNumber, ConstraintValidatorContext context) {// 实现校验逻辑return phoneNumber != null && phoneNumber.matches("^1[3-9]\\d{9}$");}
}

使用自定义注解:

public class UserDTO {@ValidPhoneNumberprivate String phone;
}

8. 结合 Hutool 工具自定义

  • 身份证号码正确性校验:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IDCard.IDCardCheck.class)
public @interface IDCard {boolean required() default true;String message() default "请输入正确的身份证号码";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};/*** 校验规则*/@Componentclass IDCardCheck implements ConstraintValidator<IDCard, String> {private boolean required;@Overridepublic void initialize(IDCard constraintAnnotation) {this.required = constraintAnnotation.required();}@Overridepublic boolean isValid(String idCard, ConstraintValidatorContext constraintValidatorContext) {// 非必填if (!required) {return true;}// 使用 Hutool 的工具return IdcardUtil.isValidCard(idCard);}}
}
  • 电话号码正确性校验:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = Phone.PhoneCheck.class)
public @interface Phone {boolean required() default true;String message() default "请输入正确的手机号码";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};/*** 校验规则*/@Componentclass PhoneCheck implements ConstraintValidator<Phone, String> {private boolean required;@Overridepublic void initialize(Phone constraintAnnotation) {this.required = constraintAnnotation.required();}@Overridepublic boolean isValid(String phone, ConstraintValidatorContext constraintValidatorContext) {// 非必填if (!required) {return true;}// 使用 Hutool 的工具return PhoneUtil.isPhone(phone);}}
}

9. 国际化支持

9.1 创建消息文件

src/main/resources 目录下创建文件:

ValidationMessages.properties         # 默认消息文件
ValidationMessages_zh_CN.properties   # 中文消息文件
ValidationMessages_en_US.properties   # 英文消息文件
ValidationMessages_ja_JP.properties   # 日文消息文件

9.2 文件内容示例

ValidationMessages.properties

# 通用消息
user.id.null=用户ID不能为空
user.name.size=用户名长度必须在{min}-{max}个字符之间
user.age.range=年龄必须在{min}到{max}岁之间
user.email.invalid=请输入有效的电子邮件地址
user.password.pattern=密码必须包含大小写字母和数字,且长度至少8位# 自定义注解消息
phone.invalid=手机号格式不正确,请输入11位有效手机号

ValidationMessages_zh_CN.properties

user.id.null=用户ID不能为空
user.name.size=用户名长度必须在{min}到{max}个字符之间

9.3 在注解中引用消息

public class UserDTO {@NotNull(message = "{user.id.null}")private Long id;@Size(min = 2, max = 20, message = "{user.name.size}")private String username;@Min(value = 18, message = "{user.age.range}")@Max(value = 120, message = "{user.age.range}")private Integer age;@Email(message = "{user.email.invalid}")private String email;@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "{user.password.pattern}")private String password;@Phone(message = "{phone.invalid}") // 自定义注解private String phone;
}

9.4 参数化消息

消息中可以包含参数,参数会在运行时被替换:

user.name.size=用户名长度必须在{min}-{max}个字符之间
user.age.range=年龄必须在{min}到{max}岁之间

注解中的参数会自动填充到消息中:

@Size(min = 2, max = 20, message = "{user.name.size}")
private String username;  // 显示:用户名长度必须在2-20个字符之间

9.5 国际化关键实现

Spring Boot 会自动根据请求的 Accept-Language 头选择对应的消息文件:

  1. 请求头 Accept-Language: zh-CN → 使用 ValidationMessages_zh_CN.properties
  2. 无匹配或默认 → 使用 ValidationMessages.properties
9.5.1 Locale 解析器配置
@Configuration
public class LocaleConfig {// 基于请求头的解析器@Beanpublic LocaleResolver localeResolver() {AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();resolver.setDefaultLocale(Locale.ENGLISH);return resolver;}// 消息源配置@Beanpublic MessageSource messageSource() {ResourceBundleMessageSource source = new ResourceBundleMessageSource();source.setBasename("ValidationMessages");source.setDefaultEncoding("UTF-8");source.setUseCodeAsDefaultMessage(true);return source;}
}
9.5.2 自定义消息插值器
public class I18nMessageInterpolator implements MessageInterpolator {private final MessageSource messageSource;private final LocaleResolver localeResolver;public I18nMessageInterpolator(MessageSource messageSource, LocaleResolver localeResolver) {this.messageSource = messageSource;this.localeResolver = localeResolver;}@Overridepublic String interpolate(String messageTemplate, Context context) {return interpolate(messageTemplate, context, Locale.getDefault());}@Overridepublic String interpolate(String messageTemplate, Context context, Locale locale) {try {// 解析消息键(去掉花括号)if (messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {String messageKey = messageTemplate.substring(1, messageTemplate.length() - 1);return messageSource.getMessage(messageKey, resolveArguments(context), locale);}return messageTemplate;} catch (NoSuchMessageException e) {return messageTemplate;}}private Object[] resolveArguments(Context context) {// 从校验注解中提取参数(如@Size的min/max)if (context.getConstraintDescriptor().getAnnotation() instanceof Size) {Size size = (Size) context.getConstraintDescriptor().getAnnotation();return new Object[] {context.getPropertyPath().toString(), // 字段名size.max(),size.min()};}return new Object[0];}
}
9.5.3 注册自定义校验器
@Bean
public Validator validator(MessageSource messageSource, LocaleResolver localeResolver) {LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();factoryBean.setMessageInterpolator(new I18nMessageInterpolator(messageSource, localeResolver));return factoryBean;
}
9.5.4 使用建议
  1. 统一管理:将所有校验消息集中到 ValidationMessages 文件中
  2. 命名规范:使用 对象.字段.校验类型 的命名方式(如 user.email.invalid
  3. 避免硬编码:不要在注解中直接写消息内容,全部通过消息键引用
  4. 参数化消息:利用 {min}, {max} 等占位符使消息更灵活
  5. 多语言支持:为每种语言提供单独的消息文件

完毕。

相关文章:

  • 如何将内网的IP地址映射到外网?常见方法及详细步骤
  • 如何查看与设置电脑静态IP地址:完整指南
  • NODE-I916 I721模块化电脑发布,AI算力与超低功耗的完美平衡
  • 如何删除 HP 笔记本电脑中的所有数据:3 种解决方案说明
  • 汽车生产中的测试台连接 – EtherCAT 转CANopen高效的网关通信
  • 深入浅出:Spring Cloud Gateway 扩展点实践指南
  • 视频太大?用魔影工厂压缩并转MP4,画质不打折!
  • 探秘汽车门槛梁内板右后段成型工艺
  • Index-AniSora论文速读:探索Sora时代动画视频生成的前沿
  • Java实践:调用jar包里的方法
  • 【全解析】EN18031标准下的RLM恢复机制
  • 【人工智能】人工智能的幕后英雄:大模型训练的挑战与突破
  • Linux:进程信号---信号的概念与产生
  • BERT 作为Transformer的Encoder 为什么采用可学习的位置编码
  • 企业级 Hosts 自动化管理实战:基于 HTTP 检测的高可用域名解析方案
  • 使用 LibreOffice 实现各种文档格式转换(支持任何开发语言调用 和 Linux + Windows 环境)[全网首发,保姆级教程,建议收藏]
  • GMSL:汽车里的音视频传输
  • lambda架构和kappa架构区别
  • JAVA基础——程序流程控制(分支结构)
  • 在 Excel 中使用东方仙盟软件————仙盟创梦IDE
  • 电商运营和网站运营对比/网店推广的渠道有哪些
  • 网站设计模板设计其他/策划营销
  • 微信授权登录第三方网站开发/seo网站seo
  • 邯郸市网络建站/seo3的空间构型
  • 鲅鱼圈网站建设/原创代写文章平台
  • 有了域名如何做网站/获客渠道有哪些