0
votes

I have a dropwizard service that accepts a PUT of a JSON document representing a bean. I make use of constraint annotations in the bean implementation and use the @Valid annotation in my Resource method:

@PUT
public Response write(@Valid MyBean bean);

and everything works rather well.

Now however, I want to pass an array of objects in the JSON. I simply changed the method signature to

@PUT
public Response write(@Valid List<MyBean> beans);

and it generally works, but if there are validation errors then the response is not very user friendly. For example, If I post in 100 beans in the array, and one of them is missing a 'name' property, then the response is

{"errors":["name may not be empty"]}

without any indication of which bean in the request has the problem.

Is there a way to overcome this?

Failing that, is there a way for me to obtain a validator in the Resource class so that I can handle all this myself?

1

1 Answers

0
votes

The way you want this might not be possible. Here is a standalone test of what is happening and how Hibernate wold like for you to hook into it:

public class ListBeanValidationTest {

    public static void main(String[] args) {
        List<Bean> asList = Arrays.asList(new Bean("test"), new Bean("test2"));

        Validator v = Validators.newConfiguration().messageInterpolator(new MsgInterpolator()).buildValidatorFactory().getValidator();

        // this is what DW does but i am lazy so you get the main version
        if(asList instanceof Iterable) {
            Iterable values = (Iterable) asList;
            Set<ConstraintViolation<Object>> violations = new HashSet<>();
            for(Object value : values) {
                violations.addAll(v.validate(value));
            }

            violations.forEach(System.out::println);
        }
    }

    public static class Bean {

        Bean(String name) {
            this.name = name;
        }

        String name;

        @NotEmpty(message="bean not empty ${name}")
        String wrong;
    }

    public static class MsgInterpolator implements MessageInterpolator {

        @Override
        public String interpolate(String messageTemplate, Context context) {
            return interpolate(messageTemplate, context, Locale.getDefault());
        }

        @Override
        public String interpolate(String messageTemplate, Context context, Locale locale) {
            Object validatedValue = context.getValidatedValue();


            return null;
        }

    }
}

The MessageInterpolator is the issue here. It does not have an instance of the bean at the time it is interpolating the message for you, so sadly you can not use a reference to that. That means that you will have to do this in another way.

You can obtain a reference to your validator (how you do this is up to you, you can find it in the DW environment. I use DI via Guice for that).

If you wanted to do this the DW way, you can look into extending: JacksonMessageBodyProvider - this is the class that does the validation for DW resources.

I hope that helps

You can also read up on the options of validation that are available to you here: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-message-interpolation.html#section-custom-message-interpolation

-- artur