Springboot Bean Validation校验框架

我们的校验框架通常用于DTO层,用户从前端通过Json串传入值,后端对传入的Json封装到DTO层中,我们对DTO层的元素进行校验,比如邮箱,手机号等等。

首先需要引入bean校验需要的jar包:

<!-- Validation 相关依赖 -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.16.Final</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.6</version>
        </dependency>

怎么用?看下面的例子

@Data
public class UserDTO implements Serializable {
    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = -5087359471567830690L;

    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空!",
            groups = {InsertValidationGroup.class})
    private String username;

    /**
     * 用户密码
     */
    @NotBlank(message = "密码不能为空!",
            groups = {InsertValidationGroup.class})
    @Length(min = 6, max = 18,
            message = "密码长度不能少于6位,不能多于18位!")
    private String password;

    /**
     * 邮箱
     */
    @NotEmpty(message = "邮箱不能为空!",
            groups = {InsertValidationGroup.class})
    @Email(message = "必须为有效邮箱!")
    private String email;

    /**
     * 年龄
     */
    @NotNull(message = "年龄不能为空!",
            groups = {InsertValidationGroup.class})
    @Max(value = 60, message = "年龄不能大于60岁!")
    @Min(value = 18, message = "年龄不能小于18岁!")
    private Integer age;

    /**
     * 手机号
     */
    @NotBlank(message = "手机号不能为空!",
            groups = {InsertValidationGroup.class})
    private String phone;

    /**
     * 版本号
     */
    @NotNull(message = "版本号不能为空!",
            groups = {UpdateValidationGroup.class})
    private Long version;

    /**
     * 创建时间
     */
    private LocalDateTime created;

如果没有遵循校验框架的注解,就会抛出message中的异常信息。

常用的约束注解:

  • 空值校验类: @Null, @NotNull, @NotEmpty, @NotBlank等
  • 范围校验类: @Min, @Size, @Digits, @Future, @Negative等
  • 其他校验类: @Email, @URL, @AssertTrue, @Pattern等

上面可以看到 g r o u p s = U p d a t e V a l i d a t i o n G r o u p . c l a s s groups = {UpdateValidationGroup.class} groups=UpdateValidationGroup.class的代码,这个是分组校验。也就是说,比如主键注册的时候的无需填入,可以为空值,不需要在插入的时候校验。比如上面的版本号(乐观锁),只是在更新的时候校验。控制层可以这么写:

public ResponseResult update(
            @NotNull @PathVariable("id") Long id,
            @Validated(UpdateValidationGroup.class)
            @RequestBody UserDTO userDTO) {
        int update = userService.update(id, userDTO);

        if (update == 1) {
            return ResponseResult.success("更新成功!");
        } else {
            return ResponseResult.failure(ErrorCodeEnum.UPDATE_FAILURE);
        }
    }

当然,还需要创建两个空的接口分组:

public interface UpdateValidationGroup {
}
public interface InsertValidationGroup {
}

我们还可以自定义校验注解:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * 自定义手机号约束注解
 */
@Documented
// 注解的作用
@Target({ElementType.FIELD})
// 注解的保留策略
@Retention(RetentionPolicy.RUNTIME)
// 与非约束注解的不同之处
// 约束注解关联验证器类
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {

    // 约束注解验证时输出的信息
    String message() default "手机号校验错误";

    // 约束注解在验证时所属的组别
    Class<?>[] groups() default {};

    // 约束注解的有效负载
    Class<? extends Payload>[] payload() default {};
}

/**
 * 自定义手机号约束注解关联验证器
 */
public class PhoneValidator implements ConstraintValidator<Phone, String> {

    /**
     * 自定义校验逻辑方法
     * @param s
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        // 演示demo
        // 手机号验证规则 158...
        String check = "158\\d{8}";
        Pattern regex = Pattern.compile(check);

        String phone = Optional.ofNullable(s).orElse("");

        Matcher matcher = regex.matcher(phone);
        return matcher.matches();
    }
}

我们捕捉到了异常之后,下面对异常进行统一的处理:

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 拦截业务类异常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = BusinessException.class)
    public ResponseResult businessExceptionHandle(BusinessException e) {
        log.error("捕捉到业务类异常:", e);

        return ResponseResult.failure(e.getCode(), e.getMessage());
    }


    /**
     * 拦截运行时异常
     * @param e
     */
    @ResponseBody
    @ExceptionHandler(value = RuntimeException.class)
    public ResponseResult runtimeExceptionHandle(RuntimeException e) {
        log.error("捕捉到运行时异常:", e);

        return ResponseResult.failure(
                ErrorCodeEnum.UNKNOWN_ERROR.getCode(),
                e.getMessage());
    }

    /**
     * 捕捉系统级异常
     * @param th
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Throwable.class)
    public ResponseResult throwableHandle(Throwable th) {
        log.error("捕捉Throwable异常:", th);

        return ResponseResult.failure(
                ErrorCodeEnum.SYSTEM_ERROR.getCode(),
                th.getMessage());
    }

}

统一的结果封装:

@Data
@ApiModel(
        value = "统一返回结果实体",
        description = "封装统一返回结果信息实体"
)
public class ResponseResult<T> implements Serializable {

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 7813356989387725160L;

    /**
     * 是否成功
     */
    @ApiModelProperty(
            name = "success",
            value = "是否成功",
            required = true,
            dataType = "Boolean"
    )
    private Boolean success;

    /**
     * 编码
     */
    @ApiModelProperty(
            name = "code",
            value = "编码",
            required = false,
            dataType = "String"
    )
    private String code;

    /**
     * 描述信息
     */
    @ApiModelProperty(
            value = "描述信息"
    )
    private String message;

    /**
     * 结果
     */
    @ApiModelProperty(
            value = "泛型结果T"
    )
    private T result;

    /**
     * 成功
     * @param result
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> success(T result) {
        ResponseResult<T> responseResult = new ResponseResult<>();

        responseResult.setSuccess(Boolean.TRUE);
        responseResult.setResult(result);

        return responseResult;
    }

    /**
     * 失败
     * @param code
     * @param message
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> failure(String code, String message) {
        ResponseResult<T> responseResult = new ResponseResult<>();

        responseResult.setSuccess(Boolean.FALSE);
        responseResult.setCode(code);
        responseResult.setMessage(message);

        return responseResult;
    }

    /**
     * 失败
     * @param codeEnum
     * @param <T>
     * @return
     */
    public static <T> ResponseResult<T> failure(ErrorCodeEnum codeEnum) {
        return failure(codeEnum.getCode(), codeEnum.getMessage());
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值