仅当其他字段为 false 时才验证字段
Validate fields only if other field is false
假设我有一个 @ConfigurationProperties
class 需要根据另一个字段的值验证一组字段。例如,SdkProperties
有一个 enabled
字段。只有当 enabled
为 true
时,才应验证其他字段。
SdkProperties
@Configuration
@ConfigurationProperties(...)
@Data
@Validated
public class SdkProperties
{
private boolean enabled;
@NotEmpty
private String apiKey
// ... etc.
}
仅当 enabled
为 true
时才应验证 @NotEmpty
注释。
执行此操作的正确方法是什么?
我看过使用 @AssertTrue
和 isValid
函数手动处理验证的示例。但我不想那样做。
我想知道使用验证组是否可行?
您可以编写自定义 ConstraintValidator
。
@Configuration
@ConfigurationProperties(prefix = "sdk")
@Validated
@NotEmptyWhenEnabled // <----- custom validation -----
@Data
class SdkProperties {
private boolean enabled;
private String apiKey;
}
@Constraint(validatedBy = {NotEmptyWhenEnabledValidator.class})
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface NotEmptyWhenEnabled {
String message() default "SDK apiKey needed when SDK is enabled";
Class[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class NotEmptyWhenEnabledValidator implements ConstraintValidator<NotEmptyWhenEnabled,SdkProperties> {
@Override
public boolean isValid(SdkProperties sdkProperties,
ConstraintValidatorContext constraintValidatorContext) {
boolean enabled = sdkProperties.isEnabled();
boolean empty = null == sdkProperties.getApiKey() || sdkProperties.getApiKey().isEmpty();
return !enabled || (enabled && !empty);
}
}
然后当 SDK 被启用 但 API 键没有 时,你会在启动时收到一条很好的消息。
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'sdk' to so.demo.SdkProperties$$EnhancerBySpringCGLIB$ecd6003 failed:
Reason: SDK apiKey needed when SDK is enabled
Action:
Update your application's configuration
Process finished with exit code 0
编辑
自 spring-boot-2.2.0.RELEASE
(2019 年 10 月 16 日)起,您有了另一个选择。
您可以在构造函数中验证属性。
通过使用:不可变的@ConfigurationProperties 绑定
Configuration properties now support constructor-based binding, which allows a @ConfigurationProperties
-annotated class to be immutable. Constructor-based binding can be enabled by annotating a @ConfigurationProperties
class or one of its constructors with @ConstructorBinding
. Annotations such as @DefaultValue
and @DateTimeFormat
can be used on constructor parameters that are provided by configuration property binding.
参考:boot-features-external-config-constructor-binding
所以在你的情况下......
@ConfigurationProperties(prefix = "sdk")
class SdkProperties {
private boolean enabled;
private String apiKey;
@ConstructorBinding
public SdkProperties(boolean enabled, String apiKey) {
this.enabled = enabled;
this.apiKey = apiKey;
// direct validation in the constructor
boolean apiKeyNullOrEmpty = null == apiKey || apiKey.isEmpty();
Assert.isTrue(!enabled || !apiKeyNullOrEmpty, "When SDK is enabled, a SDK-api key is mandatory!");
}
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getApiKey() {return apiKey; }
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
}
假设我有一个 @ConfigurationProperties
class 需要根据另一个字段的值验证一组字段。例如,SdkProperties
有一个 enabled
字段。只有当 enabled
为 true
时,才应验证其他字段。
SdkProperties
@Configuration
@ConfigurationProperties(...)
@Data
@Validated
public class SdkProperties
{
private boolean enabled;
@NotEmpty
private String apiKey
// ... etc.
}
仅当 enabled
为 true
时才应验证 @NotEmpty
注释。
执行此操作的正确方法是什么?
我看过使用 @AssertTrue
和 isValid
函数手动处理验证的示例。但我不想那样做。
我想知道使用验证组是否可行?
您可以编写自定义 ConstraintValidator
。
@Configuration
@ConfigurationProperties(prefix = "sdk")
@Validated
@NotEmptyWhenEnabled // <----- custom validation -----
@Data
class SdkProperties {
private boolean enabled;
private String apiKey;
}
@Constraint(validatedBy = {NotEmptyWhenEnabledValidator.class})
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface NotEmptyWhenEnabled {
String message() default "SDK apiKey needed when SDK is enabled";
Class[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class NotEmptyWhenEnabledValidator implements ConstraintValidator<NotEmptyWhenEnabled,SdkProperties> {
@Override
public boolean isValid(SdkProperties sdkProperties,
ConstraintValidatorContext constraintValidatorContext) {
boolean enabled = sdkProperties.isEnabled();
boolean empty = null == sdkProperties.getApiKey() || sdkProperties.getApiKey().isEmpty();
return !enabled || (enabled && !empty);
}
}
然后当 SDK 被启用 但 API 键没有 时,你会在启动时收到一条很好的消息。
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'sdk' to so.demo.SdkProperties$$EnhancerBySpringCGLIB$ecd6003 failed:
Reason: SDK apiKey needed when SDK is enabled
Action:
Update your application's configuration
Process finished with exit code 0
编辑
自 spring-boot-2.2.0.RELEASE
(2019 年 10 月 16 日)起,您有了另一个选择。
您可以在构造函数中验证属性。
通过使用:不可变的@ConfigurationProperties 绑定
Configuration properties now support constructor-based binding, which allows a
@ConfigurationProperties
-annotated class to be immutable. Constructor-based binding can be enabled by annotating a@ConfigurationProperties
class or one of its constructors with@ConstructorBinding
. Annotations such as@DefaultValue
and@DateTimeFormat
can be used on constructor parameters that are provided by configuration property binding.
参考:boot-features-external-config-constructor-binding
所以在你的情况下......
@ConfigurationProperties(prefix = "sdk")
class SdkProperties {
private boolean enabled;
private String apiKey;
@ConstructorBinding
public SdkProperties(boolean enabled, String apiKey) {
this.enabled = enabled;
this.apiKey = apiKey;
// direct validation in the constructor
boolean apiKeyNullOrEmpty = null == apiKey || apiKey.isEmpty();
Assert.isTrue(!enabled || !apiKeyNullOrEmpty, "When SDK is enabled, a SDK-api key is mandatory!");
}
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getApiKey() {return apiKey; }
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
}