3

I have a spring controller :

@RequestMapping(value = "bulk", method = RequestMethod.POST)
@ResponseBody
public APIResponse createBulkEnquiries(@Valid @RequestBody List<BulkDTO> bulkDTOs) {
    // some code 
}

It is not validating any of the bulkDTOs as @valid do not work on element of Collection directly (although BulkDTO is validatable). Also I can not wrap List in some other class (which works) like

public class ValidatableObjectsCollectionWrapper {
    @Valid
    List<BulkDTO> bulkDTOs;
}

because it will change input json format. So I need some other way around.

I also tried to make a custom validator for collection

public class CollectionValidator implements Validator {

private final SpringValidatorAdapter validator;

public CollectionValidator(SpringValidatorAdapter validator) {
    super();
    this.validator = validator;
}

@Override
public boolean supports(Class<?> clazz) {
    return Collection.class.equals(clazz);
}

@Override
public void validate(Object target, Errors errors) {

    Collection<Object> objectCollection = (Collection<Object>) target;

    for (Object object : objectCollection) {
        validator.validate(object, errors);
    }
}
}

But Don't know how to invoke or bind it on the controller param.

Sushil Kumar
  • 115
  • 2
  • 7
  • I think spring doesnot validate collection elements using @valid. I think you can implement custom validator where you can have your own validations by iterating or streaming through the list. – Rohith K Jan 03 '16 at 15:17
  • I tried it, Don't know how to invoke it (see the updated question). – Sushil Kumar Jan 03 '16 at 16:34
  • IMHO this is http://stackoverflow.com/questions/34011892/spring-validation-for-requestbody-parameters-bound-to-collections-in-controller/36790509 an elegant solution. Hope it helps. – eruiz Apr 22 '16 at 09:52

2 Answers2

6

Not sure if it's the only, or the best solution, but you can use a wrapper object, without having to change the JSON, using the @JsonValue and @JsonCreator annotations. Here is a complete example:

public class BulkDTOWrapper {

    private List<BulkDTO> bulks;

    @JsonCreator
    public BulkDTOWrapper(List<BulkDTO> bulks) {
        this.bulks = bulks;
    }

    public BulkDTOWrapper() {
    }

    @JsonValue
    public List<BulkDTO> getBulks() {
        return bulks;
    }

    public void setBulks(List<BulkDTO> bulks) {
        this.bulks = bulks;
    }

    public static void main(String[] args) throws IOException {
        BulkDTO b1 = new BulkDTO("hello");
        BulkDTO b2 = new BulkDTO("world");

        BulkDTOWrapper wrapper = new BulkDTOWrapper();
        wrapper.setBulks(Arrays.asList(b1, b2));

        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(wrapper);
        System.out.println("json = " + json);

        BulkDTOWrapper wrapper2 = om.readValue(json, BulkDTOWrapper.class);
        System.out.println(wrapper2.getBulks().size());
    }
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
2

Actually, this can be achieved using Spring Validation and JSR303.

*Expose a MethodValidationPostProcessor bean.

Annotate your controller class with @Validated (org.springframework.validation.annotation.Validated)

Use the JSR303 validation annotations on your MyEntity fields/methods.

Annotate your RequestBody argument with @Valid (you've already done this in your example).

Add an @ExceptionHandler method to handle MethodArgumentNotValidException. This can be done in the controller or in a @ControllerAdvice class.*

abhinavsinghvirsen
  • 1,853
  • 16
  • 25