-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SpringValidatorAdapter
fails in getRejectedValue
if ValueExtractor
used in property path to unwrap a container type
#29043
Comments
The workaround:I created a subclass of @Slf4j
public class WorkaroundLocalValidatorFactoryBean extends LocalValidatorFactoryBean {
@Override
protected Object getRejectedValue(@NonNull String field, @NonNull ConstraintViolation<Object> violation,
@NonNull BindingResult bindingResult) {
try {
return super.getRejectedValue(field, violation, bindingResult);
} catch (Exception e) {
log.warn("Workaround for spring-projects/spring-framework#29043: {}", e.getMessage());
return violation.getInvalidValue();
}
}
} The original @Nullable
protected Object getRejectedValue(String field, ConstraintViolation<Object> violation, BindingResult bindingResult) {
Object invalidValue = violation.getInvalidValue();
if (!field.isEmpty() && !field.contains("[]") &&
(invalidValue == violation.getLeafBean() || field.contains("[") || field.contains("."))) {
// Possibly a bean constraint with property path: retrieve the actual property value.
// However, explicitly avoid this for "address[]" style paths that we can't handle.
invalidValue = bindingResult.getRawFieldValue(field);
}
return invalidValue;
} Maybe it's possible to add more conditions here, if it turns out that generic support of custom field-wrapper types will not be possible? I'm not sure why |
I'm facing a similar problem but not sure if it is exactly the same or if I should raise another issue. I created a minimal example: public class Foo {
private Map<Class<?>, @Valid Bar> instanceExamples;
// Omitted constructors, getters and setters
}
public class Bar {
@NotBlank
@Length(max = 5)
public String value;
// Omitted constructors, getters and setters
} @SpringBootApplication
@RestController
public class SpringValidationReproducerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringValidationReproducerApplication.class, args);
}
@Bean
ApplicationRunner run(Validator validator) {
return args -> {
Foo foo = new Foo(
Map.of(Bar.class, new Bar("Invalid instance example because it is too long")));
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(foo, "foo");
validator.validate(foo, bindingResult);
System.out.println("bindingResult: " + bindingResult);
};
}
} Stacktrace:
From what I could tell, Spring could not convert the String |
Hi, I'm facing exactly same issues as @xak2000 mentions. I'm using validation of |
Finally I've found the issue I'm having. I am also using a custom reference class with semantics similar to import jakarta.validation.Configuration;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.MessageInterpolator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.validation.ValidationConfigurationCustomizer;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WorkaroundLocalValidatorFactoryBean extends LocalValidatorFactoryBean {
private final List<ValidationConfigurationCustomizer> customizers;
@Override
protected Object getRejectedValue(
@NonNull String field,
@NonNull ConstraintViolation<Object> violation,
@NonNull BindingResult bindingResult
) {
try {
return super.getRejectedValue(field, violation, bindingResult);
} catch (Exception e) {
log.warn("Workaround for spring-projects/spring-framework#29043: {}", e.getMessage());
return violation.getInvalidValue();
}
}
@Override
protected void postProcessConfiguration(@NonNull Configuration<?> configuration) {
super.postProcessConfiguration(configuration);
customizers.forEach(customizer -> customizer.customize(configuration));
}
} |
Bean Validation uses registered In the meantime, I'll have a look at ways to improve |
SpringValidatorAdapter
fails in getRejectedValue
if ValueExtractor
used in property path to unwrap a container type
At this point I slightly out of the context of this task. @rstoyanchev Do you mean that |
The code to to access the property value was introduced in this change aedccec. The commit goes back a long time but tests do fail if removed. The case as explained in the code comment and in #13536 (comment) has to do with bean level constraints that refer to properties like in this example for a failing test. There are already protective checks for containers like |
Affects: 5.3.22
Related issue: #16855.
This issue is basically the same as #16855, but for a custom wrapper (#16855 only handles
Optional
).SpringValidatorAdapter
couldn't process ConstraintViolations because it can't traverse property path. It throws this exception:But the problem in this case not with getter/setter, but with the fact that actual field type is not simple POJO, but a custom wrapper class.
Example:
Note, that
NullableOptionalValueExtractor
is registered usingMETA-INF/services/javax.validation.valueextraction.ValueExtractor
as described here and correctly unwraps the field value for validation purposes.@UnwrapByDefault
makes validation annotations (e.g.@NotNull
) onNullableOptional
field work as if they were on a wrapped type (Location
in this example).And the validation example, that throws mentioned exception:
The validation itself that Hibernate-validator performs works fine. But then
SpringValidatorAdapter
throws. Looks like it isn't aware ofValueExtractor
concept.What can be done in this case to make it work (or at least not throw)? Can the support of custom wrapper types be added in some generic way that will work out of the box (just like it's done in Hibernate validator thanks to
ValueExtractor
)? Or, at least, skip the extraction of a value if not possible instead of throw the error.What workaround exists for now? I tried to debug the source code and unfortunately didn't found any possibility for a workaround.
The main goal is to use
NullableOptional<T>
(or any other custom wrapper) for fields of JSON body ofPATCH
requests, where a field value could be either null, non-null or absent (when field is absent in JSON representation altogether) and business logic needs to distinguish between all 3 cases. Sometimes people useOptional<T>
for this purpose, but it's error-prone and ugly, as third state ofOptional
field is the null-reference, that is anti-pattern and usually unexpected usage ofOptional
type. Custom wrapper type solves these problems as it's intended to be used specifically for the mentioned case and will never reference tonull
(but could containnull
inside).Minimal Demo Project: custom-container-validation-bug.zip
The text was updated successfully, but these errors were encountered: