I am using Quarkus with Jakarta, resteasy-reactive-jackson and Hibernate Orm.. but afiak that doesnt make a difference.
When you have any quarkus endpoint that takes json data in the post body and you then tell quarkus to map that json data to a class-instance by providing a type other then JsonObject in the function, quarkus automatically handles the conversion and any occurring errors.
@POST
@Path("/create")
@Transactional
@Consumes(MediaType.APPLICATION_JSON)
public Response create(UserDTO data){
return Response.ok().build();
}
So if in the example above the JsonData from the postbody will be mapped to a UserDTO. Now if the UserDTO requires the property isMinor to be a boolean but a String is provided: quarkus will handle that error and show something like this:
{
"objectName": "Class",
"attributeName": "isMinor",
"line": 2,
"column": 16,
"value": "string"
}
how can I transform this into a usable error?
The obvious approach would be a ExceptionMapper but even with a global implementation like this. Quarkus/hibernate seemingly handles the Json conversion before everything else. Even 404s get caught by the mapper but the json conversion error isnt.
Here i am trying to use my own Response system but that doesn't really matter.
@Provider
public class GlobalExceptionMapper implements ExceptionMapper<Throwable> {
@Override
public Response toResponse(Throwable throwable) {
return CCResponse.error(new CCStatus(1100, "Something bad happend :c"));
}
}
As a workaround I started using JsonObjects and mapping them to the desired type on my own
ClassName instance = null;
String dataString = String.valueOf(data);
ObjectMapper objectMapper = new ObjectMapper();
try {
instance = objectMapper.readValue(dataString, ClassName.class);
} catch (JsonProcessingException e) {
throw new CCException(1106, e.getMessage());
}
This does work but is more code and more ugly.. there has to be a better way to to this that I am not seeing.
Thanks in advance for any help.
Analysis
I wrote a little reproducer to force this behaviour, in essence with
RequestDto.java:Resource.java:If we now
POSTto this endpoint with a non-boolean value for"foo":We get a response similar to this:
It is worth mentioning that the return code is a
400 BAD REQUEST, not a500 INTERNAL SERVER ERROR. This indicates that most probably a (default) mapper for this exception-type (we will see which one in the next section) already exists. If this is true, this is the reason why the catch-all mapper is not triggered.Enabling debug logging by setting
quarkus.log.level=DEBUGinapplication.propertiesand re-running thecurl-command from above shows the following logsSo we see that an
InvalidFormatExceptionhas been thrown.Handling this exception
To handle this specific exception, we can write a custom mapper for the
InvalidFormatException. I opted to use methodgetOriginalMessage()since it seem to provide a human-readable error message. I also opted to create a dedicatedErrorResponse-class:ErrorResponse.java:InvalidFormatExceptionMapper.java:Testing
With these changes in place, we can re-run the
curl-command:And get a human-readable error: