Consider a simple RestController that requires a @Valid
object as show in the example below.
@RestController
public class ErrorProne {
@PostMapping("/customers")
Customer customerEcho(@Valid @RequestBody Customer c) {
return c;
}
}
class Customer {
@NotBlank(message = "name can't be blank")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Executing an http POST :8080/customers name=
results in a lot of superfluous details about the validation error including internal spring state.
{
"error": "Bad Request",
"errors": [
{
"arguments": [
{
"arguments": null,
"code": "name",
"codes": [
"customer.name",
"name"
],
"defaultMessage": "name"
}
],
"bindingFailure": false,
"code": "NotBlank",
"codes": [
"NotBlank.customer.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"defaultMessage": "name can't be blank",
"field": "name",
"objectName": "customer",
"rejectedValue": ""
}
],
"message": "Validation failed for object='customer'. Error count: 1",
"path": "/customers",
"status": 400,
"timestamp": "2020-03-08T16:19:45.828+0000"
}
It would be nicer to only send back errors field with only the essential details for example.
{
"error": "Bad Request",
"errors": [
{
"defaultMessage": "name can't be blank",
"field": "name",
"objectName": "customer",
"rejectedValue": ""
}
],
"message": "Validation failed for object='customer'. Error count: 1",
"path": "/customers",
"status": 400,
"timestamp": "2020-03-08T16:19:45.828+0000"
}
While it's possible to implement a custom bean of DefaultErrorAttributes
I think improving the default behavior is the better thing for users.
Comment From: asaikali
The following Jackson Serializer can reduce the amount of noise in the output
@JsonComponent
public class ItemSerializer extends StdSerializer<FieldError> {
public ItemSerializer() {
this(null);
}
public ItemSerializer(Class<FieldError> t) {
super(t);
}
@Override
public void serialize(
FieldError value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("field", value.getField());
jgen.writeStringField("defaultMessage",value.getDefaultMessage());
jgen.writeStringField("objectName",value.getObjectName());
jgen.writeObjectField("rejectedValue",value.getRejectedValue());
jgen.writeEndObject();
}
}
for http POST :8080/customers name=
produces
{
"error": "Bad Request",
"errors": [
{
"defaultMessage": "name can't be blank",
"field": "name",
"objectName": "customer",
"rejectedValue": ""
}
],
"message": "Validation failed for object='customer'. Error count: 1",
"path": "/customers",
"status": 400,
"timestamp": "2020-03-08T20:33:11.716+0000"
}
Comment From: wilkinsona
Thanks for the suggestion.
What is superfluous from your perspective may be required by someone else. I don't think we can make this change to the defaults without running the risk of breaking existing applications. For example, they may be using the values in errors.[].codes
to look up some localized text that is displayed in a web UI.
If the information that is returned by default exceeds your needs, customization using your own ErrorAttributes
implementation or a Jackson serialiser as you have shown above is the way to go.