سؤال

I'm using Spring MVC 3 and JSR 303. I have a form backing object that has beans of different types in it. Depending on a request parameter value, I'll have to choose a bean to validate and save. I can't use @Valid for validation since the bean to validate is not known until the runtime.

I was able to inject a javax.validation.Validator to the controller, but I'm not sure how to validate a bean with it and store any errors in a BindingResult/Error in a "Spring way".

I need to do it in the handler method, rather than an initBinder method, because of request mapping.

[edit]

The problem I'm having with validate(Object, Errors) is that it doesn't recognize nested beans. The bean to validate is accessed through foo.getBar().getBean(), where the foo is the form backing object. When I do validate(foo.getBar().getBean(), errors), I get the following error message.

JSR-303 validated property 'property-name' does not have a corresponding accessor for Spring data binding

Has anyone done something like this before? Thanks.

هل كانت مفيدة؟

المحلول

Just a guess, but have you tried

 errors.pushNestedPath("bar.bean"); // Path to the nested bean
 validate(foo.getBar().getBean(), errors)
 errors.popNestedPath();

That's how BindingResult is usually used for validation of the nested beans.

نصائح أخرى

Yup, the magic class you're looking for is org.springframework.validation.beanvalidation.SpringValidatorAdapter

This class gets a javax.validation.Validator injected into it and contains the code for, as its name implies, 'adapting' the output back into the familiar Errors object. It's what's being used internally to do the processing when you put @Valid on a method parameter.

You can get them directly by adding an explicit LocalValidatorFactoryBean in your dispatcher servlet. Just inject an instance of that as an instance of the standard Spring Validator interface and use it like you would any 'pre jsr-303' spring validation provider.

The way the I've seen this done is to use a standard JSR-303 validator (whatever you're already injecting in) to get the violations (i.e. Set<ConstraintViolaion<T>>)

Then use code similar to that inside LocalValidatorFactoryBean to convert between those violations and Spring Errors:

public static <T> void convert(Errors errors, Collection<ConstraintViolation<T>> violations) {
        for (ConstraintViolation<?> violation : violations) {
            String field = violation.getPropertyPath().toString();
            FieldError fieldError = errors.getFieldError(field);
            if (fieldError == null || !fieldError.isBindingFailure()) {
                errors.rejectValue(field, violation.getConstraintDescriptor().getAnnotation().annotationType()
                        .getSimpleName(), getArgumentsForConstraint(errors.getObjectName(), field, violation
                        .getConstraintDescriptor()), violation.getMessage());
            }
        }
    }

    private static Object[] getArgumentsForConstraint(String objectName, String field,
            ConstraintDescriptor<?> descriptor) {
        List<Object> arguments = new LinkedList<Object>();
        String[] codes = new String[] { objectName + Errors.NESTED_PATH_SEPARATOR + field, field };
        arguments.add(new DefaultMessageSourceResolvable(codes, field));
        arguments.addAll(descriptor.getAttributes().values());
        return arguments.toArray(new Object[arguments.size()]);
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top