快速入门
快速入门 - 非SpringBoot环境
1.引入依赖
<!-- Bean Validation 的实现 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- el表达式语言支持 -->
<dependency>
<groupId>org.glassfish.expressly</groupId>
<artifactId>expressly</artifactId>
<version>5.0.0</version>
</dependency>
2.测试
/**a
* 用户添加DTO
*/
@Data
public class UserAddDTO {
/**
* 账号
*/
@NotBlank(message="账号不能为空")
@Length(min = 5, max = 16, message = "账号长度为 5-16 位")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
private String username;


/**
* 密码
*/
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
}
public class DTOTest {
public static void main(String[] args) {
// 定义校验对象
UserAddDTO userAddDTO = new UserAddDTO();
userAddDTO.setUsername("123");
userAddDTO.setPassword("123");
// 获取校验工厂
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
// 校验
Set<ConstraintViolation<UserAddDTO>> validate = validator.validate(userAddDTO);
validate.forEach(x->
System.out.println(x.getMessage())
);
}
}
快速入门-SpringBoot 环境
1.引入依赖
<!-- web环境 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- Bean Validation starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.测试
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
private Logger logger = LoggerFactory.getLogger(getClass());
@GetMapping("/get")
public void get(@RequestParam("id") @Min(value = 1L, message = "编号必须大于 0") Integer id) {
logger.info("[get][id: {}]", id);
}
}
实战功能
处理校验异常
在实际开发中,我们需要对校验结果统一返回给前端。
实现思路:在校验失败后,validation会抛出异常,所以通过SpringBoot的全局异常捕获机制来统一捕获校验异常返回给前端
1.定义统一返回结果类
/**
* 通用返回结果
*
* @param <T> 结果泛型
*/
public class CommonResult<T> implements Serializable {
public static Integer CODE_SUCCESS = 0;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 返回数据
*/
private T data;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> CommonResult<T> error(CommonResult<?> result) {
return error(result.getCode(), result.getMessage());
}
public static <T> CommonResult<T> error(Integer code, String message) {
Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!");
CommonResult<T> result = new CommonResult<>();
result.code = code;
result.message = message;
return result;
}
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>();
result.code = CODE_SUCCESS;
result.data = data;
result.message = "";
return result;
}
}
2.定义异常枚举
/**
* 业务异常枚举
*/
public enum ServiceExceptionEnum {
// ========== 系统级别 ==========
SUCCESS(0, "成功"),
SYS_ERROR(2001001000, "服务端发生异常"),
MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"),
INVALID_REQUEST_PARAM_ERROR(2001001002, "请求参数不合法"),
// ========== 用户模块 ==========
USER_NOT_FOUND(1001002000, "用户不存在"),
;
/**
* 错误码
*/
private final int code;
/**
* 错误提示
*/
private final String message;
ServiceExceptionEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
3.定义全局异常捕获类
@ControllerAdvice(basePackages = "com.zwf.module.validation")
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理Exception 异常
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {
// 记录异常日志
logger.error("[exceptionHandler]", e);
// 返回 ERROR CommonResult
return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),
ServiceExceptionEnum.SYS_ERROR.getMessage());
}
/**
* 处理 ConstraintViolationException 数据校验异常
*/
@ResponseBody
@ExceptionHandler(value = ConstraintViolationException.class)
public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
logger.debug("[constraintViolationExceptionHandler]", ex);
// 拼接错误
StringBuilder detailMessage = new StringBuilder();
for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
// 使用 ; 分隔多个错误
if (detailMessage.length() > 0) {
detailMessage.append(";");
}
// 拼接内容到其中
detailMessage.append(constraintViolation.getMessage());
}
// 包装 CommonResult 结果
return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage);
}
}
4.测试结果
{
"code": 2001001002,
"message": "请求参数不合法:编号必须大于 0",
"data": null
}
自定义校验规则
1.创建自定义注解
定义一个新的注解、它用于标记需要进行自定义校验的字段、方法或者参数
@Documented
@Constraint(validatedBy = MobileNoValidator.class) // 指定校验器
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER}) // 指定校验对象
@Retention(RetentionPolicy.RUNTIME) // 指定校验时机
public @interface MobileNo {
String message() default "手机号码格式不正确"; // 校验失败提示信息
Class<?>[] groups() default {}; // 校验分组
Class<? extends Payload>[] payload() default {}; // 负载信息,用于传递额外的信息
}
2.创建校验器
/**
* 约束校验器
*/
public class MobileNoValidator implements ConstraintValidator<MobileNo, String> {
// 以1开头、后面跟着10位数组
private static final String PHONE_REGEX = "^1\\d{10}$";
@Override
public void initialize(MobileNo constraintAnnotation) {
// 初始化
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(value == null || value.isEmpty()) {
return false;
}
return value.matches(PHONE_REGEX);
}
}
3.使用自定义注解
@Data
public class UserDTO {
@NotNull
private Long id;
@NotNull
@MobileNo
private String mobileNo;
}
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
@PostMapping("/test")
public String test(@Valid @RequestBody UserDTO userVo) {
System.out.println(userVo);
return "success";
}
}
4.拦截自定义注解异常
@ControllerAdvice(basePackages = "com.zwf.module.validation")
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public CommonResult hasndleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
logger.debug("[handleMethodArgumentNotValidException]", ex);
StringBuilder detailMessage = new StringBuilder();
for (FieldError error : ex.getBindingResult().getFieldErrors()) {
if (!detailMessage.isEmpty()) {
detailMessage.append(";");
}
detailMessage.append("【").append(error.getField()).append(":").append(error.getDefaultMessage()).append("】");
}
return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage);
}
}
5.测试结果
{
"code": 2001001002,
"message": "请求参数不合法:【mobileNo:手机号码格式不正确】",
"data": null
}
Bean Validation 定义的约束注解
百度 推荐 juejin.cn/post/739913…