SpringBoot整合hibernate validator实现自定义参数校验并控制校验顺序

1. jar包导入

//validator
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.7.Final</version>
</dependency>

2. 自定义注解

  • 根据需要校验参数,这里校验用户的角色是否合法
package com.example.handlerinterceptor.annotation;

import com.example.handlerinterceptor.validator.IdentifyRoleValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
//配置校验类
@Constraint(validatedBy = IdentifyRoleValidator.class)
public @interface IdentifyRole {
    String message() default "{javax.validation.constraints.IdentifyRole.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

3. 校验类

  • 实现ConstraintValidator接口,指定具体的注解名称和属性类型
package com.example.handlerinterceptor.validator;

import com.example.handlerinterceptor.annotation.IdentifyRole;
import com.example.handlerinterceptor.enums.RoleEnum;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class IdentifyRoleValidator implements ConstraintValidator<IdentifyRole, String> {

    @Override
    public void initialize(IdentifyRole constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    /**
     * 通过枚举类校验用户角色
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return RoleEnum.containsValue(value);
    }

}

4. 角色枚举类

  • 只能输入vip,customer,dealers三种角色
package com.example.handlerinterceptor.enums;

import java.util.Objects;

public enum RoleEnum {

    VIP("vip"), CUSTOMER("customer"), DEALERS("dealers");

    private String value;

    RoleEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public static boolean containsValue(String role) {
        for (RoleEnum roleEnum : RoleEnum.values()) {
            if (Objects.equals(role,roleEnum.getValue())) {
                return true;
            }
        }
        return false;
    }

}

5. 校验注解排序

  • 针对同时使用多个注解,而且需要指定校验顺序的场景
  • 定义一个接口,然后通过@GroupSequence注解,按顺序添加排好顺序后的类
package com.example.handlerinterceptor.group;

public interface GroupA {}

package com.example.handlerinterceptor.group;

public interface GroupB {}

  • 这里第一个为Default,即将没有指定顺序的参数校验注解作为第一个需要校验的注解,然后为GroupA和GroupB,可以根据需要继续添加GroupC、D…等
package com.example.handlerinterceptor.group;

import javax.validation.GroupSequence;
import javax.validation.groups.Default;

@GroupSequence({Default.class, GroupA.class, GroupB.class})
public interface Group {}

6. 实体类使用参数校验注解

package com.example.handlerinterceptor.sysuser.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.example.handlerinterceptor.annotation.IdentifyRole;
import com.example.handlerinterceptor.group.GroupA;
import com.example.handlerinterceptor.group.GroupB;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public class SysUser extends Model<SysUser> {

    @TableId(type = IdType.AUTO)
    private Long userId;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;

    @TableLogic
    @JsonIgnore
    private Integer delFlag;

    private String name;

    private Integer age;

    @NotBlank(message = "interest must not be blank")
    private String interest;

    @NotBlank(message = "role must not be blank", groups = {GroupA.class})
    @IdentifyRole(message = "role is illegal", groups = {GroupB.class})
    private String role;

    /**
     * 获取主键值
     *
     * @return 主键值
     */
    @Override
    public Serializable pkVal() {
        return this.userId;
    }

}

7. Controller具体使用

  • 入参前添加@Validated({Group.class}),表示根据Group类中指定的顺序进行参数校验
@PostMapping
public AjaxResult insert(@RequestBody @Validated({Group.class}) SysUser sysUser) {
    return AjaxResult.ok(this.sysUserService.save(sysUser));
}

8. 全局异常拦截

  • 需要配合全局异常拦截,遇到非法入参,进行友好提示
package com.example.handlerinterceptor.handler;

import com.example.handlerinterceptor.dto.AjaxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    public class AjaxReult {
        private Integer code;
        private String msg;


        public AjaxReult(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(ValidationException.class)
    public AjaxReult handleValidationException(ValidationException e) {
        LOGGER.error(e.getMessage(), e);
        return new AjaxReult(1, e.getCause().getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        LOGGER.error(e.getMessage(), e);
        return AjaxResult.error(e.getBindingResult().getFieldError().getDefaultMessage());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public AjaxReult handleConstraintViolationException(ConstraintViolationException e) {
        LOGGER.error(e.getMessage(), e);
        return new AjaxReult(1, e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public AjaxReult handlerNoFoundException(Exception e) {
        LOGGER.error(e.getMessage(), e);
        return new AjaxReult(404, "路径不存在,请检查路径是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public AjaxReult handleDuplicateKeyException(DuplicateKeyException e) {
        LOGGER.error(e.getMessage(), e);
        return new AjaxReult(1, "数据重复,请检查后提交");
    }

    @ExceptionHandler(Exception.class)
    public AjaxReult handleException(Exception e) {
        LOGGER.error(e.getMessage(), e);
        return new AjaxReult(500, "系统繁忙,请稍后再试");
    }

}

9. 请求接口

  • 先输入一个非法的角色,抛出异常,被全局异常处理类捕获,根据注解中的message信息进行提示

在这里插入图片描述

  • 输入一个空字符串,因为@NotBlank(message = “role must not be blank”, groups = {GroupA.class}),所有会优先判断是否为空,然后再判断是否非法

在这里插入图片描述
在这里插入图片描述

  • 角色合法不为空,兴趣输入空字符串,会校验是否为空,这里因为在Group类中第一个指定了先校验Default
  • 因为在接口入参处@Validated({Group.class})指定根据Group中的配置进行校验,如果不指定Default,则只有注解中groups指定了GroupA和GroupB的注解才会被校验

在这里插入图片描述

  • 传入合法的参数,校验通过,请求成功

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值