I have the following controller method:
@RequestMapping(value=\"/map/update\", method=RequestMethod.POST, produces = \"application/json; charset=utf-8\")
@Re
The @Valid
annotation can be used inside the diamond operator:
private List<@Valid MyType> types;
or
@Valid
private List<MyType> types;
Now, every list item will be validated.
I'm using spring-boot 1.5.19.RELEASE
I annotate my service with @validated
and then apply @Valid
to the List
parameter in the method and items in my list get validated.
Model
@Data
@ApiModel
@Validated
public class SubscriptionRequest {
@NotBlank()
private String soldToBpn;
@NotNull
@Size(min = 1)
@Valid
private ArrayList<DataProducts> dataProducts;
private String country;
@NotNull
@Size(min = 1)
@Valid
private ArrayList<Contact> contacts;
}
Service Interface (or use on concrete type if no interface)
@Validated
public interface SubscriptionService {
List<SubscriptionCreateResult> addSubscriptions(@NonNull @Size(min = 1) @Valid List<SubscriptionRequest> subscriptionRequestList)
throws IOException;
}
Global Exception Handler method (ApiError Type is not my design)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseBody
public ApiError[] handleConstraintViolationException(ConstraintViolationException exception) {
List<InvalidField> invalidFields = exception.getConstraintViolations().stream()
.map(constraintViolation -> new InvalidField(constraintViolation.getPropertyPath().toString(),
constraintViolation.getMessage(),
constraintViolation.getInvalidValue()))
.collect(Collectors.toList());
return new ApiError[] {new ApiError(ErrorCodes.INVALID_PARAMETER, "Validation Error", invalidFields)};
}
example bad method call from a controller
LinkedList<SubscriptionRequest> list = new LinkedList<>();
list.add(new SubscriptionRequest());
return subscriptionService.addSubscriptions(list);
Response body (note the index [0])
[
{
"errorCode": "invalid.parameter",
"errorMessage": "Validation Error",
"invalidFields": [
{
"name": "addSubscriptions.arg0[0].soldToBpn",
"message": "may not be empty",
"value": null
},
{
"name": "addSubscriptions.arg0[0].dataProducts",
"message": "may not be null",
"value": null
},
{
"name": "addSubscriptions.arg0[0].contacts",
"message": "may not be null",
"value": null
}
]
}
]
I think the most elegant solution is to create a custom Validator for Collection and a @ControllerAdvice that registers that Validator in the WebDataBinders, take a look to Spring validation for RequestBody parameters bound to collections in Controller methods
I would suggest to wrap your List categories into some DTO bean and validate it. Beside of working validation you will benefit from more flexible API.
@RequestMapping(value="/map/update", method=RequestMethod.POST, produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntityWrapper updateMapTheme(
HttpServletRequest request,
@RequestBody @Valid TagRequest tagRequest,
HttpServletResponse response
) throws ResourceNotFoundException, AuthorizationException {
...
}
public static class TagRequest {
@Valid
List<CompanyTag> categories;
// Gettes setters
}
Validating a collection does not work directly.
For example: what should it do if multiple elements fail the validation? Stop after first validation? Validate all (if so what is to be done with the collection of messages)?
If in your configuration Spring delegates to a Bean Validator provider like Hibernate Validator, you should look up for ways of implementing a collection validator there.
For Hibernate, a similar problem is discussed here
use @Validated annotate controller
use @Valid annotate @RequestBody