七、spring boot 添加注解校验@Valid
常用的验证注解注解


- 引入maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 实体添加注解
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Data
public class UserDTO implements Serializable {
@NotNull
@NotBlank
@Size(min = 4, max = 50, message = "用户名长度必须在4到50个字符之间")
private String username;
@NotNull
@NotBlank
@Size(min = 6, max = 20, message = "密码长度必须在6到20个字符之间")
private String password;
@NotNull
@NotBlank
@Size(min = 6, max = 20, message = "密码长度必须在6到20个字符之间")
private String matchingPassword;
@NotNull
@Email // 此处的@email注解只是做了简单的规则校验,实际应用还是需要换为正则表达式比较合适
private String email;
}
- 请求接口参数添加@Valid注解,开启验证
@PostMapping("/register")
@ApiOperation("注册")
public UserDTO register(@RequestBody @Valid UserDTO userDTO) {
return userDTO;
}
- 当请求参数不符合规范时,返回错误消息
模拟参数:
{
"email" : "",
"matchingPassword": "123456",
"password" : "123456",
"username" : "aep"
}
响应结果:
{
"timestamp": "2023-04-01T14:22:28.167+00:00",
"status": 400,
"error": "Bad Request",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.uaa.dto.UserDTO com.example.uaa.rest.AuthorizeController.register(com.example.uaa.dto.UserDTO): [Field error in object 'userDTO' on field 'username': rejected value [aep]; codes [Size.userDTO.username,Size.username,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDTO.username,username]; arguments []; default message [username],50,4]; default message [用户名长度必须在4到50个字符之间]] ...",
"message": "Validation failed for object='userDTO'. Error count: 1",
"errors": [
{
"codes": [
"Size.userDTO.username",
"Size.username",
"Size.java.lang.String",
"Size"
],
"arguments": [
{
"codes": [
"userDTO.username",
"username"
],
"arguments": null,
"defaultMessage": "username",
"code": "username"
},
50,
4
],
"defaultMessage": "用户名长度必须在4到50个字符之间",
"objectName": "userDTO",
"field": "username",
"rejectedValue": "aep",
"bindingFailure": false,
"code": "Size"
}
],
"path": "/authorize/register"
}
自定义校验注解
创建一个自定义注解,名称为@ValidEmail
import com.example.uaa.myvalidation.EmailValidation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EmailValidation.class) // 指定具体的类去处理校验
@Documented
public @interface ValidEmail {
String message() default "无效的email格式"; // 可使用国际化key名称, 例如:"{javax.validation.constraints.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
创建一个注解处理类EmailValidation.java
package com.example.uaa.myvalidation;
import com.example.uaa.myvalidation.annotation.ValidEmail;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;
/**
* @ValidEmail 注解校验逻辑
*/
public class EmailValidation implements ConstraintValidator<ValidEmail, String> {
private final static String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
@Override
public void initialize(ValidEmail constraintAnnotation) { }
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
var compile = Pattern.compile(EMAIL_REGEX);
return compile.matcher(value).matches();
}
}
使用注解
@Data
public class UserDTO implements Serializable {
...
@NotNull
@ValidEmail // 使用自定义的email校验注解
private String email;
...
}
使用passay密码工具,自定义复杂的密码验证规则
创建一个自定义注解,名称为@ValidPassword
import com.example.uaa.myvalidation.PasswordConstraintValidation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordConstraintValidation.class) // 指定具体的类去处理校验
@Documented
public @interface ValidPassword {
String message() default "无效的密码格式"; // 可使用国际化key名称, 例如:"{javax.validation.constraints.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
创建一个注解处理类PasswordConstraintValidation.java
需要用到的工具如下:
<!--密码验证工具 https://www.passay.org/-->
<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<version>1.6.3</version>
</dependency>
package com.example.uaa.myvalidation;
import com.example.uaa.myvalidation.annotation.ValidPassword;
import org.passay.*;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author: aesop
* @description: 校验密码具体规则
* @date: 2023/4/3 19:14
*/
public class PasswordConstraintValidation implements ConstraintValidator<ValidPassword, String> {
@Override
public void initialize(ValidPassword constraintAnnotation) { }
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
//passay 官方文档 使用教程 :https://www.passay.org/reference/
PasswordValidator validator = new PasswordValidator(
// 长度在8-16个字符之间
new LengthRule(8, 16),
// 至少有一个大写字母
new CharacterRule(EnglishCharacterData.UpperCase, 1),
// 至少有一个小写字母
new CharacterRule(EnglishCharacterData.LowerCase, 1),
// 至少有一个数字
new CharacterRule(EnglishCharacterData.Digit, 1),
// 至少有一个特殊字符
new CharacterRule(EnglishCharacterData.Special, 1),
// 非法:字母按序列连续出现超过5个 (>=5)
new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),
// 非法:数字按序列连续超过5个 (>=5)
new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false),
// 非法:按键盘布局连续相邻出现5个 (>=5)
new IllegalSequenceRule(EnglishSequenceData.USQwerty, 5, false),
// 不准有空白字符
new WhitespaceRule());
var validate = validator.validate(new PasswordData(value));
return validate.isValid();
}
}
实体类字段加上注解
@ValidPassword
private String password;
校验两次密码是否匹配
匹配两次密码是否相同
创建一个自定义注解,名称为@ValidPasswordMatching
import com.example.uaa.myvalidation.PasswordConstraintMatchingValidation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordConstraintMatchingValidation.class) // 指定具体的类去处理校验
@Documented
public @interface ValidPasswordMatching {
String message() default "两次密码不匹配"; // 可使用国际化key名称, 例如:"{javax.validation.constraints.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
创建一个注解处理类PasswordConstraintMatchingValidation.java
import com.example.uaa.dto.UserDTO;
import com.example.uaa.myvalidation.annotation.ValidPasswordMatching;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author: aesop
* @description: 校验两次密码是否匹配
* @date: 2023/4/3 19:13
*/
public class PasswordConstraintMatchingValidation implements ConstraintValidator<ValidPasswordMatching, UserDTO> {
@Override
public void initialize(ValidPasswordMatching constraintAnnotation) { }
@Override
public boolean isValid(UserDTO userDTO, ConstraintValidatorContext constraintValidatorContext) {
return userDTO.getPassword().equals(userDTO.getMatchingPassword());
}
}
将注解@ValidPasswordMatching放到类上面
import com.example.uaa.myvalidation.annotation.ValidEmail;
import com.example.uaa.myvalidation.annotation.ValidPassword;
import com.example.uaa.myvalidation.annotation.ValidPasswordMatching;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Data
@ValidPasswordMatching
public class UserDTO implements Serializable {
@NotNull
@ValidPassword
private String password;
@NotNull
private String matchingPassword;
}

浙公网安备 33010602011771号