'How to handle "org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error" in Spring?
I'm adding an API to an existing Spring Boot project using @RestController.
My Spring Boot experience is: This is my first time using it.
The RestController works fine when I make a properly formed request but throws an error if the request is not correct (this is what I would expect). However, I can't figure out how to give a meaningful error response to a user.
This is the Controller class:
@RestController
public class SomeController {
@PostMapping(value = "/update")
@ResponseBody
public ResponseEntity update(@RequestBody Config config) {
//do stuff
return ResponseEntity.ok(//response);
}
}
This is the Config class:
@Getter
@Setter
@AllArgsConstructor
@Builder
public class Config {
private String thing1;
private String thing2;
}
If I run the application and hit localhost:8080/update
with:
{
"thing1": "dog",
"thing2": "cat",
}
Then everything is all good and we can //do stuff.
However if I hit localhost:8080/update
with:
{
"thing1": "dog",
"thing2": 123,
}
I get an exception: .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type...
This is expected since I provided a number and a String was needed for thing2.
The response I get is: <h1>sorry</h1>
I'm not sure if this is a Spring default or configured somewhere in the application (if it is I cannot find it).
I want to change that to something meaningful like: "You gave an incorrect value for thing2".
But since the code never reaches //do stuff I'm not sure how to handle this?
I tried to make an exception class with:
@ControllerAdvice
@ExceptionHandler(HttpMessageNotReadableException.class)
But I get a "Cannot resolve symbol HttpMessageNotReadableException" from the IDE. I'm not even sure if making an exception class is the right way to handle it though?
Edit: In this tutorial (https://www.toptal.com/java/spring-boot-rest-api-error-handling) the Spring default message is something like:
{
"timestamp": 1500597044204,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "JSON parse error: Unrecognized token 'three': was expecting ('true', 'false' or 'null'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'aaa': was expecting ('true', 'false' or 'null')\n at [Source: java.io.PushbackInputStream@cba7ebc; line: 4, column: 17]",
"path": "/birds"
}
But I just get <h1>sorry</h1>
. I actually want something more like the above.
Solution 1:[1]
This should work but Spring is not meant to handle error before the HTTP request can be deserialized correctly. You can write rules on request data after deserialization using annotation on your DTO class's fields like @NotNull, @NotEmpty, @Positive, ...
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class ErrorController {
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<String> handleException(HttpMessageNotReadableException exception, HttpServletRequest request) {
return new ResponseEntity("You gave an incorrect value for ....", HttpStatus.BAD_REQUEST);
}
}
Solution 2:[2]
My preferred solution would be to override the handleHttpMessageNotReadable method :) instead of using @ExceptionHandlers
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
//handle exception
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | allMeow |