Reputation: 1662
Using JAX-RS, I have successfully implemented an ExceptionMapper for Exceptions that do not require a more sophisticated response than an HTTP status code, as follows.
@Provider
public class ISBNNotFoundManager implements ExceptionMapper<ISBNNotFoundException>{
@Override
public Response toResponse(ISBNNotFoundException exception) {
return Response.status(NOT_FOUND).build();
}
}
This works as expected.
However, I want to respond with something more useful when bean validation fails. The follow code snippet results in a MessageBodyProviderNotFoundException.
@Provider
public class ConstraintViolationExceptionMapper implements
ExceptionMapper<ConstraintViolationException> {
@Override
@Produces(MediaType.APPLICATION_JSON)
public Response toResponse(ConstraintViolationException exception) {
final Map<String, String> errorResponse =
exception.getConstraintViolations()
.stream()
.collect(
Collectors.toMap(o -> o.getPropertyPath().toString(), o -> o.getMessage()));
return Response.status(Response.Status.BAD_REQUEST).entity(errorResponse).build();
}
}
When a bean validation occurs the response includes the HTTP response code 500 and the root cause is given as follow:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException:
MessageBodyWriter not found for media type=application/json,
type=class java.util.HashMap, genericType=class java.util.HashMap.
What I have tried that didn't work:
Wrapping the Map in a GenericEntity like so. The same result as above:
new GenericEntity>(errorResponse) {}
What I tried the DID work:
Wrapping the map in a custom POJO, DataIntegrityValidation, as follows:
@XmlRootElement
public class DataIntegrityValidation {
private Map<String, String> errorResponse = new HashMap<>();
public Map<String, String> getErrorResponse() {
return errorResponse;
}
public void setErrorResponse(Map<String, String> errorResponse) {
this.errorResponse = errorResponse;
}
}
Then in the toResponse method I wrap the map in the DataIntegrityValidation POJO like so and add it to the response object.
DataIntegrityValidation dataIntegrityValidation =
new DataIntegrityValidation();
dataIntegrityValidation.setErrorResponse(errorResponse);
return
Response.status(Response.Status.BAD_REQUEST)
.entity(dataIntegrityValidation).build();
This gives the following JSON:
{
"errorResponse": {
"entry": [
{
"key": "saveBook.arg0.description",
"value": "size must be between 100 and 2147483647"
},
{
"key": "saveBook.arg0.published",
"value": "must be in the past"
},
{
"key": "saveBook.arg0.link",
"value": "must match \"^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$\""
}
]
}
}
I can live with this but would really like to know why it cannot handle the Map even though it is wrapped in the Generic Entity.
All responses welcome.
Upvotes: 0
Views: 342
Reputation: 3943
The reason the marshalling failed for both Map and GenericEntity is because there is no JAXB definition associated with them. And when you wrapped the map in a POJO annotated with @XmlRootElement
; it was able to marshal it correctly.
Upvotes: 1