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
- add
@Validated
to your controller
- use
@Valid @NotNull @RequestBody List<@Valid Pojo> pojoList
in your controller method signature
This 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:
- in the wrapper, use
@JsonValue
annotation on the wrapped list field
- add a constructor taking a list as argument, and annotate it with
@JsonCreator
- in your controller method, use
@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 [...]"
}