Spring Boot 验证实体类两个中的一个不为空或者一个非空另一个必须为空

问题说明

对接银行接口的时候经常有平台订单号和银行订单号二选一即可,普通的验证注解满足不了,需要自己实现

解决方案

1. 两个中的一个不为空

创建自定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = AtLeastOneNotEmptyValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface AtLeastOneNotEmpty {
    String message() default "At least one of the fields must be not empty";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String[] fieldNames();
}
创建验证器类
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;

public class AtLeastOneNotEmptyValidator implements ConstraintValidator<AtLeastOneNotEmpty, Object> {

    private String[] fieldNames;

    @Override
    public void initialize(AtLeastOneNotEmpty constraintAnnotation) {
        this.fieldNames = constraintAnnotation.fieldNames();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        try {
            for (String fieldName : fieldNames) {
                Field field = value.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Object fieldValue = field.get(value);
                if (fieldValue != null && !fieldValue.toString().trim().isEmpty()) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}
使用
/**
 *
 * 支付入参基类,新增需要继承
 * @author zsp
 * @date 2024/05/07
 */
@Data
@AtLeastOneNotEmpty(
    fieldNames = {"orderId", "bankDeductionOrderId"},
    message = "订单号或者银商订单号不能为空",
    groups = {Pay.class, Query.class, Refund.class}
)
public class PayBo {

使用@Validated注解即可触发

2. 一个非空另一个必须为空

创建自定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = OneNotEmptyOneEmptyValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface OneNotEmptyOneEmpty {
    String message() default "One field must be not empty and the other must be empty";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String firstField();
    String secondField();
}
创建验证器类
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;

public class OneNotEmptyOneEmptyValidator implements ConstraintValidator<OneNotEmptyOneEmpty, Object> {

    private static final Logger LOGGER = Logger.getLogger(OneNotEmptyOneEmptyValidator.class.getName());

    private String firstFieldName;
    private String secondFieldName;

    @Override
    public void initialize(OneNotEmptyOneEmpty constraintAnnotation) {
        this.firstFieldName = constraintAnnotation.firstField();
        this.secondFieldName = constraintAnnotation.secondField();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        try {
            Field firstField = value.getClass().getDeclaredField(firstFieldName);
            Field secondField = value.getClass().getDeclaredField(secondFieldName);

            firstField.setAccessible(true);
            secondField.setAccessible(true);

            Object firstFieldValue = firstField.get(value);
            Object secondFieldValue = secondField.get(value);

            boolean isFirstFieldNotEmpty = firstFieldValue != null && !firstFieldValue.toString().trim().isEmpty();
            boolean isSecondFieldNotEmpty = secondFieldValue != null && !secondFieldValue.toString().trim().isEmpty();

            return (isFirstFieldNotEmpty && !isSecondFieldNotEmpty) || (!isFirstFieldNotEmpty && isSecondFieldNotEmpty);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            return false;
        }
    }
}
使用
@OneNotEmptyOneEmpty(firstField = "field1", secondField = "field2", message = "field1 and field2 cannot both be empty or both be filled")
public class MyEntity {

    private String field1;
    private String field2;

使用@Validated注解即可触发

注意

异常捕获的时候要注意,跟原有异常信息不太一致,需要修改为如下

/**
 * 自定义验证异常
 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public JgResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    log.error(e.getMessage());
    if(e.getBindingResult().getFieldError() == null){
       //两个中的一个不为空的情况,e.getBindingResult().getFieldError()为null会报错
        return JgResponse.fail(StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", "));
    }else {
        return JgResponse.fail(e.getBindingResult().getFieldError().getDefaultMessage());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值