在SpringBoot中怎么验证输入请求的自定义注解

发布时间:2022-02-25 15:09:53 作者:小新
来源:亿速云 阅读:224

这篇文章主要介绍了在SpringBoot中怎么验证输入请求的自定义注解,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

在我们的日常编程中,我们会使用许多可用于验证的 Spring Boot 默认注解,如@NotNull、@Size、@NotBlank、@Digits等等,这是验证任何传入的一种很酷的方式要求。

考虑一个场景,默认情况下有一些字段是可选的,如果其他一些字段由特定值填充,则它必须是强制性的。 

Spring 没有为这种验证预定义注释。

让我们举一些例子,看看我们如何简化验证过程,使其代码可重用,并在注释级别引入抽象。 

在一个典型的销售平台中,会有销售操作和无效销售操作。该金额在销售操作中是强制性的,在销售操作无效的情况下,冲销类型将是强制性的。 

我们的 dto 类如下:

public class IncomingRequestDto {    
    public TransactionType transactionType;
    public ReversalType reversalType;
    public String reversalId;
    public AmountDto amountDto;
}

IncomingRequestDto 有几个属性,如 transactionType、reversalType 作为 ENUMS。

public enum TransactionType {
    SALE {
        public String toString() {
            return "Sale";
        }
    },
    VOIDSALE {
        public String toString() {
            return "VoidSale";
        }
    },
}
public enum ReversalType {
    TIMEDOUT {
        public final String toString() {
            return "Timedout";
        }
    },
    CANCELLED {
        public final String toString() {
            return "Cancelled";
        }
    }
}

和 amountDto 为:

public class AmountDto {    
    public String value;
}

场景一: amountDto.value 是有条件的。当我们收到一个具有 transactionType="SALE" 的请求时,amountDto.value 应该是强制性的。

场景 2: reversalType 是有条件的。当我们收到一个具有 transactionType="VOIDSALE" 的请求时,reversalType 应该是强制性的。 

让我们首先定义一个带有验证过程所需属性的注释:

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
public @interface NotNullIfAnotherFieldHasValue {
    String fieldName();
    String fieldValue();
    String dependFieldName();
    String message();
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        NotNullIfAnotherFieldHasValue[] value();
    }
}

fieldName 和 fieldValue 将被定义,我们必须在其上搜索特定值。这里是“销售”。

将定义dependFieldName,我们必须在其上搜索值。 

现在让我们实现上面的接口:

public class NotNullIfAnotherFieldHasValueValidator
        implements ConstraintValidator<NotNullIfAnotherFieldHasValue, Object> {
    private String fieldName;
    private String expectedFieldValue;
    private String dependFieldName;
    @Override
    public void initialize(NotNullIfAnotherFieldHasValue annotation) {
        fieldName = annotation.fieldName();
        expectedFieldValue = annotation.fieldValue();
        dependFieldName = annotation.dependFieldName();
    }
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext ctx) {
        String fieldValue = "";
        String dependFieldValue = "";
        if (value == null) {
            return true;
        }
        try {
            fieldValue = BeanUtils.getProperty(value, fieldName);
            dependFieldValue = BeanUtils.getProperty(value, dependFieldName);
            return validate(fieldValue, dependFieldValue, ctx);
        } catch (NestedNullException ex) {
            dependFieldValue = StringUtils.isNotBlank(dependFieldValue) ? dependFieldValue : "";
            try {
                return validate(fieldValue, dependFieldValue, ctx);
            } catch (NumberFormatException exception) {
                return false;
            }
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NumberFormatException | NullPointerException ex) {
            return false;
        }
    }
    private boolean validate(String fieldValue,
                             String dependFieldValue, ConstraintValidatorContext ctx) {
        if (!StringUtils.isBlank(fieldValue)) {
             if (expectedFieldValue.equals(fieldValue) && (StringUtils.isBlank(dependFieldValue))) {
                ctx.disableDefaultConstraintViolation();
                ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate())
                        .addNode(dependFieldName)
                        .addConstraintViolation();
                return false;
            }
        } else {
            return false;
        }
        return true;
    }
}

在这里,我们需要返回并使用其实现类装饰我们的界面,如下所示:

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)@Constraint(validatedBy = NotNullIfAnotherFieldHasValueValidator.class)@Documented
public @interface NotNullIfAnotherFieldHasValue {

就是这样!我们已经完成了!让我们用我们的自定义注解装饰我们的 IncomingRequestDto 类:

@JsonDeserialize(as = IncomingRequestDto.class)@NotNullIfAnotherFieldHasValue.List({
        @NotNullIfAnotherFieldHasValue(
                fieldName = "transactionType",
                fieldValue = "Sale",
                dependFieldName = "amountDto.value",
                message = " - amount is mandatory for Sale requests"),        
})@JsonInclude(JsonInclude.Include.NON_NULL)
public class IncomingRequestDto {
    public TransactionType transactionType;
    public ReversalType reversalType;
    public String reversalId;
    public AmountDto amountDto;

}

通过添加上述注释,请求将被拒绝为BAD,HTTP 400不为 Sale 类型请求填充 amountDto.value。我们可以在 List 中添加任意数量的验证,而无需更改任何代码,如下所示:

@JsonDeserialize(as = IncomingRequestDto.class)
@NotNullIfAnotherFieldHasValue.List({@NotNullIfAnotherFieldHasValue(
                fieldName = "transactionType",
                fieldValue = "Sale",
                dependFieldName = "amountDto.value",
                message = " - amount is mandatory for Sale requests"),
        @NotNullIfAnotherFieldHasValue(
                fieldName = "transactionType",
                fieldValue = "VoidSale",
                dependFieldName = "reversalType",
                message = " - Reversal Type is mandatory for VoidSale requests"),})
@JsonInclude(JsonInclude.Include.NON_NULL)
public class IncomingRequestDto {
    public TransactionType transactionType;
    public ReversalType reversalType;
    public String reversalId;
    public AmountDto amountDto;

}

感谢你能够认真阅读完这篇文章,希望小编分享的“在SpringBoot中怎么验证输入请求的自定义注解”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

推荐阅读:
  1. 怎么在SpringBoot中利用AOP处理请求日志
  2. 怎么在JAVA中通过自定义注解进行数据验证

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springboot

上一篇:linux用户分为哪几类

下一篇:Springboot项目中如何自定义工具类来实现后台上传图片的操作

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》