I'm hoping to to catch some jackson exceptions that are occurring in a spring-boot API I am developing. For example, I have the following request class and I want to catch the error that occurs when the "questionnaireResponse" key in the JSON request object is null or blank i.e " "
in the request object.
@Validated
@JsonRootName("questionnaireResponse")
public class QuestionnaireResponse {
@JsonProperty("identifier")
@Valid
private Identifier identifier = null;
@JsonProperty("basedOn")
@Valid
private List<Identifier_WRAPPED> basedOn = null;
@JsonProperty("parent")
@Valid
private List<Identifier_WRAPPED> parent = null;
@JsonProperty("questionnaire")
@NotNull(message = "40000")
@Valid
private Identifier_WRAPPED questionnaire = null;
@JsonProperty("status")
@NotNull(message = "40000")
@NotEmptyString(message = "40005")
private String status = null;
@JsonProperty("subject")
@Valid
private Identifier_WRAPPED subject = null;
@JsonProperty("context")
@Valid
private Identifier_WRAPPED context = null;
@JsonProperty("authored")
@NotNull(message = "40000")
@NotEmptyString(message = "40005")
@Pattern(regexp = "\\d{4}-(?:0[1-9]|[1-2]\\d|3[0-1])-(?:0[1-9]|1[0-2])T(?:[0-1]\\d|2[0-3]):[0-5]\\d:[0-5]\\dZ", message = "40001")
private String authored;
@JsonProperty("author")
@NotNull(message = "40000")
@Valid
private QuestionnaireResponseAuthor author = null;
@JsonProperty("source")
@NotNull(message = "40000")
@Valid
private Identifier_WRAPPED source = null; // Reference(Patient | Practitioner | RelatedPerson) resources not implemented
@JsonProperty("item")
@NotNull(message = "40000")
@Valid
private List<QuestionnaireResponseItem> item = null;
public Identifier getIdentifier() {
return identifier;
}
public void setIdentifier(Identifier identifier) {
this.identifier = identifier;
}
public List<Identifier_WRAPPED> getBasedOn() {
return basedOn;
}
public void setBasedOn(List<Identifier_WRAPPED> basedOn) {
this.basedOn = basedOn;
}
public List<Identifier_WRAPPED> getParent() {
return parent;
}
public void setParent(List<Identifier_WRAPPED> parent) {
this.parent = parent;
}
public Identifier_WRAPPED getQuestionnaire() {
return questionnaire;
}
public void setQuestionnaire(Identifier_WRAPPED questionnaire) {
this.questionnaire = questionnaire;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Identifier_WRAPPED getSubject() {
return subject;
}
public void setSubject(Identifier_WRAPPED subject) {
this.subject = subject;
}
public Identifier_WRAPPED getContext() {
return context;
}
public void setContext(Identifier_WRAPPED context) {
this.context = context;
}
public String getAuthored() {
return authored;
}
public void setAuthored(String authored) {
this.authored = authored;
}
public QuestionnaireResponseAuthor getAuthor() {
return author;
}
public void setAuthor(QuestionnaireResponseAuthor author) {
this.author = author;
}
public Identifier_WRAPPED getSource() {
return source;
}
public void setSource(Identifier_WRAPPED source) {
this.source = source;
}
public List<QuestionnaireResponseItem> getItem() {
return item;
}
public void setItem(List<QuestionnaireResponseItem> item) {
this.item = item;
}
}
Resulting in this Jackson error:
{
"Map": {
"timestamp": "2018-07-25T12:45:32.285Z",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Root name '' does not match expected ('questionnaireResponse') for type [simple type, class com.optum.genomix.model.gel.QuestionnaireResponse]; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name '' does not match expected ('questionnaireResponse') for type [simple type, class com.optum.genomix.model.gel.QuestionnaireResponse]\n at [Source: (PushbackInputStream); line: 2, column: 3]",
"path": "/api/optumhealth/genomics/v1.0/questionnaireResponse/create"
}
}
Is there a way to catch and handle these exceptions (in the example JsonRootName is null/invalid), maybe similarly to @ControllerAdvice classes extending ResponseEntityExceptionHandler?
Try something along the lines of:
@ControllerAdvice
public class ExceptionConfiguration extends ResponseEntityExceptionHandler {
@ExceptionHandler(JsonMappingException.class) // Or whatever exception type you want to handle
public ResponseEntity<SomeErrorResponsePojo> handleConverterErrors(JsonMappingException exception) { // Or whatever exception type you want to handle
return ResponseEntity.status(...).body(...your response pojo...).build();
}
}
Which allows you to handle any type of exception and respond accordingly. If the response status is always the same just stick a @ResponseStatus(HttpStatus.some_status)
on the method and call ResponseEntity.body(...)
Found this question with a similar issue, only mine was a different JSON parse error:
JSON parse error: Unrecognized character escape 'w' (code 119); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unrecognized character escape 'w' (code 119)\n at [Source: (PushbackInputStream); line: 1, column: 10]
coming from a REST JSON request like so
{"query":"\\w"}
If you can modify the Rest Controller, you can catch the JSON parse error with an HttpMessageNotReadableException
(worked for me in Spring Boot using a @RestController
annotation). Even though I could not catch the error with @ExceptionHandler(Exception.class)
You can respond with custom JSON by using a serialized object (naturally converts to JSON). You can also specify that you want the request and exception which caused the issue in the first place. So you can get details, and or modify the error message.
@ResponseBody
@ExceptionHandler(HttpMessageNotReadableException.class)
private SerializableResponseObject badJsonRequestHandler(HttpServletRequest req, Exception ex) {
SerializableResponseObject response = new SerializableResponseObject(404,
"Bad Request",
"Invalid request parameters, could not create query",
req.getRequestURL().toString())
Logger logger = LoggerFactory.getLogger(UserController.class);
logger.error("Exception: {}\t{}\t", response);
return response;
}
The code would return something like
{
"timestamp": "Thu Oct 17 10:19:48 PDT 2019",
"status": 404,
"error": "Bad Request",
"message": "Invalid request parameters, could not create query",
"path": "http://localhost:8080/user/query"
}
And would log something like
Exception: [Thu Oct 17 10:19:48 PDT 2019][404][http://localhost:8080/user/query][Bad Request]: Invalid request parameters, could not create query
Code for the SerializableResponseObject
public class SerializableResponseObject implements Serializable {
public String timestamp;
public Integer status;
public String error;
public String message;
public String path;
public SerializableResponseObject(Integer status, String error, String message, String path) {
this.timestamp = (new Date()).toString();
this.status = status;
this.error = error;
this.message = message;
this.path = path;
}
public String getTimestamp() {
return timestamp;
}
public Integer getStatus() {
return status;
}
public String getError() {
return error;
}
public String getMessage() {
return message;
}
public String getPath() {
return path;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public void setStatus(Integer status) {
this.status = status;
}
public void setError(String error) {
this.error = error;
}
public void setMessage(String message) {
this.message = message;
}
public void setPath(String path) {
this.path = path;
}
public String toString() {
return "[" + this.timestamp + "][" + this.status + "][" + this.path + "][" + this.error + "]: " + this.message;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With