Reputation: 1034
I have some value from my configuration file, that should be a JSON (which will be loaded as a String).
I'd like Spring to validate that this value is indeed a valid JSON before injecting it and throw an error else.
I'm injecting it as follows:
@Value("${source.SomeJsonString}")
private String someJsonString;
I saw the following: How to make simple property validation when using Spring @Value
However, since I have multiple classes that should be injected with source.SomeJsonString
, I wouldn't like to create a setter for each, and write the validation again and again.
Is there any way to write the validator only once?
I thought about creating annotation (Spring validate string value is a JSON), but it seems that values that are annotated with @Value
cannot be validated .
Is there any other way?
Upvotes: 3
Views: 2496
Reputation: 10711
You could insert the string as a bean, parsing/validating it first:
@Configuration
public class JsonStringPropertyConfig {
@Autowired
private ObjectMapper objectMapper;
@Bean
@Qualifier("someJsonString")
String someJsonString(@Value("${source.someJsonString}") String someJsonString) throws JsonProcessingException {
// Validate
objectMapper.readTree(someJsonString);
return someJsonString;
}
}
@Service
public class SomeService {
@Autowired
@Qualifier("someJsonString")
private String someJsonString;
}
or even simpler, with the @Resource
annotation:
@Configuration
public class JsonStringPropertyConfig {
@Autowired
private ObjectMapper objectMapper;
@Bean
String someJsonString(@Value("${source.someJsonString}") String someJsonString) throws JsonProcessingException {
// Validate
objectMapper.readTree(someJsonString);
return someJsonString;
}
}
@Service
public class SomeService {
@Resource(name = "someJsonString")
private String someJsonString;
}
Upvotes: -1
Reputation: 10315
Spring externalized configuration can be validated using JSR 303 Bean Validation API. But it requires Spring Type-safe Configuration Properties instead of @Value("${property}")
.
Add Hibernate Validator dependency to build.gradle
implementation 'org.hibernate.validator:hibernate-validator'
The type-safe configuration properties must be annotated with @Validated
and the field someJsonString
with a custom annotation @ValidJsonConstraint
@Component
@ConfigurationProperties("source")
@Validated
public class SourceProperties {
@ValidJsonConstraint
private String someJsonString;
public String getSomeJsonString() {
return someJsonString;
}
public void setSomeJsonString(String someJsonString) {
this.someJsonString = someJsonString;
}
}
You can inject the properties into all required services, so the validation code is not duplicated
@Autowired
private SourceProperties sourceProperties;
It's time to create the custom annotation
@Documented
@Constraint(validatedBy = ValidJsonValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidJsonConstraint {
String message() default "Invalid JSON";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and a validator to validate fields annotated with the custom annotation
public class ValidJsonValidator implements ConstraintValidator<ValidJsonConstraint, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void initialize(ValidJsonConstraint constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
try {
objectMapper.readTree(value);
return true;
} catch (JsonProcessingException e) {
return false;
}
}
}
When in the application.properties the source.someJsonString
value is valid JSON
source.someJsonString={"test":"qwe"}
application successfully starts.
When JSON is invalid
source.someJsonString=qwe
Application fails to start with the following exception
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'source' to intellectsoft.afgruppen.shiftschedule.SourceProperties failed:
Property: source.someJsonString
Value: qwe
Origin: class path resource [application.properties]:26:23
Reason: Invalid JSON
Action:
Update your application's configuration
Also, the same can be achieved a bit easier without JSR 303 Bean Validation API.
Create a custom validation component
@Component
public class JsonValidator {
private final ObjectMapper objectMapper = new ObjectMapper();
public boolean isValid(String value) {
try {
objectMapper.readTree(value);
return true;
} catch (JsonProcessingException e) {
return false;
}
}
}
Inject the validator and perform validation in the property setter
@Component
@ConfigurationProperties("source")
public class SourceProperties {
private final JsonValidator jsonValidator;
private String someJsonString;
public SourceProperties(JsonValidator jsonValidator) {
this.jsonValidator = jsonValidator;
}
public String getSomeJsonString() {
return someJsonString;
}
public void setSomeJsonString(String someJsonString) {
if (!jsonValidator.isValid(someJsonString)) {
throw new IllegalArgumentException(someJsonString + " is not a valid JSON");
}
this.someJsonString = someJsonString;
}
}
Upvotes: 5