In a Spring MVC REST service (json), I have a controller method like this one :
@RequestMapping(method = RequestMethod.POST, value = { \"/doesntmatter\" })
I think your best option is to wrap the list - How to validate request parameter if it is not a bean in spring MVC?
There is no way atm to say that the @Valid applies to the elements of the collection.
The only way i could find to do this is to wrap the list, this also means that the JSON input would have to change.
@RequestMapping(method = RequestMethod.POST, value = { "/doesntmatter" })
@ResponseBody
public List<...> myMethod(@Valid @RequestBody List<MyBean> request, BindingResult bindingResult) {
becomes:
@RequestMapping(method = RequestMethod.POST, value = { "/doesntmatter" })
@ResponseBody
public List<...> myMethod(@Valid @RequestBody MyBeanList request, BindingResult bindingResult) {
and we also need:
import javax.validation.Valid;
import java.util.List;
public class MyBeanList {
@Valid
List<MyBean> list;
//getters and setters....
}
This looks like it could also be possible with a custom validatior for lists but i have not got that far yet.
The @Valid annotation is part of the standard JSR-303 Bean Validation API, and is not a Spring-specific construct. Spring MVC will validate a @Valid object after binding so-long as an appropriate Validator has been configured.
Reference : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
Given Spring-Boot + Jackson for JSON serialization + org.springframework.boot:spring-boot-starter-validation
(must be included manually for spring boot >= 2.3.0)
Using built-ins
@Validated
to your controller@Valid @NotNull @RequestBody List<@Valid Pojo> pojoList
in your controller method signatureThis will throw a javax.validation.ConstraintViolationException
error on invalid beans though, which is mapped to 500 Internal Error
by default. Hence, ensure you have a ControllerAdvice
for this as well !
Using a wrapper
A list wrapper is nice (that is, a class with a single field of type List<E>
), but from the responses above you will have to change the JSON as well ({"list": []}
vs []
), which is not nice...
Solution:
@JsonValue
annotation on the wrapped list field@JsonCreator
@Valid @RequestBody ListWrapper<Pojo> tokenBodies
This works, is elegant, and doesn't require anything more. Moreover, it will throw the usual org.springframework.web.bind.MethodArgumentNotValidException
on invalid beans.
Wrapper Example (java):
(For a full example in Kotlin, see https://stackoverflow.com/a/64060909)
public class ValidList<E> {
@JsonValue
@Valid
@NotNull
@Size(min = 1, message = "array body must contain at least one item.")
private List<E> values;
@JsonCreator
public ValidList(E... items) {
this.values = Arrays.asList(items);
}
public List<E> getValues() {
return values;
}
public void setValues(List<E> values) {
this.values = values;
}
}
public class SomePojo {
@Min(value = 1)
int id;
@Size(min = 2, max = 32)
String token;
// getters and setters
}
@RestController
public class SomeController {
@PostMapping("/pojos")
public ValidList<SomePojo> test(@Valid @RequestBody ValidList<SomePojo> pojos) {
return pojos;
}
}
Submit ok:
curl -H "Content-Type: application/json" -X POST http://localhost:8080/pojos -d '[{"id": 11, "token": "something"}]'
[{"token" : "something", "id" : 11}]
Submit empty body:
curl -H "Content-Type: application/json" -X POST http://localhost:8080/ns -d '[]'
{
"timestamp" : "2020-09-25T09:55:05.462+00:00",
"error" : "Bad Request",
"message" : "Validation failed for object='validList'. Error count: 1",
"exception" : "org.springframework.web.bind.MethodArgumentNotValidException",
"path" : "/pojos",
"status" : 400,
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.demo.ValidList<com.example.demo.SomePojo> com.example.demo.SomeController.test(com.example.demo.ValidList<com.example.demo.SomePojo>): [Field error in object 'validList' on field 'values': rejected value [[]]; codes [Size.validList.values,Size.values,Size. [...]"
}
Submit invalid items:
curl -H "Content-Type: application/json" -X POST http://localhost:8080/ns -d '[{"id": -11, "token": ""}]'
{
"timestamp" : "2020-09-25T09:53:56.226+00:00",
"error" : "Bad Request",
"message" : "Validation failed for object='validList'. Error count: 2",
"exception" : "org.springframework.web.bind.MethodArgumentNotValidException",
"path" : "/pojos",
"status" : 400,
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.demo.ValidList<com.example.demo.SomePojo> com.example.demo.SomeController.test(com.example.demo.ValidList<com.example.demo.SomePojo>) with 2 errors: [Field error in object 'validList' on field 'values[0].id': rejected value [-11]; co [...]"
}
@Valid @RequestBody List<MyBean> request
works for me so long as you submitting valid json:-
[
{
"property1": "value1",
"property2": "value2"
},
{
"property1": "value3",
"property2": "value4"
}
]