Brief follow-up to
Spring MVC and JSR 303 Validation error customization
.
In addition to the previously shown
@ExceptionHandler(MethodArgumentNotValidException.class)
method for handling JSR-303 Validation errors, we also want to handle JSON Parsing and Mapping errors by returning similarly helpful and clean JSON responses.
Jackson apparently uses two different Exception trees for the different kinds of errors we see,
JsonParseException
and
JsonMappingException
, both of which are wrapped in
HttpMessageNotReadableException
.
Our initial attempt was to just return the
getOriginalMessage()
string from both of those, along with the
getPath()
information when it exists. But it turns out that at least a couple of cases we want to handle produce messages with more detail than we want to reveal. (Namely, the fully-qualified class name):
Using a Java
enum
to limit input values to acceptable ones
Not accepting unknown input fields
So, combining all of the cases we see today, this appears to give useful, appropriate validation error responses:
* Common handling of JSON parsing/mapping exceptions. Care is used to not return error
* details that would reveal internal Java package/class names.
@ExceptionHandler
(
HttpMessageNotReadableException
.
class
)
public
ResponseEntity
<
BaseResponseMessage
>
processConversionException
(
HttpMessageNotReadableException
e
)
{
String
msg
=
null
;
Throwable
cause
=
e
.
getCause
();
if
(
cause
instanceof
JsonParseException
)
{
JsonParseException
jpe
=
(
JsonParseException
)
cause
;
msg
=
jpe
.
getOriginalMessage
();
// special case of JsonMappingException below, too much class detail in error messages
else
if
(
cause
instanceof
MismatchedInputException
)
{
MismatchedInputException
mie
=
(
MismatchedInputException
)
cause
;
if
(
mie
.
getPath
()
!=
null
&&
mie
.
getPath
().
size
()
>
0
)
{
msg
=
"Invalid request field: "
+
mie
.
getPath
().
get
(
0
).
getFieldName
();
// just in case, haven't seen this condition
else
{
msg
=
"Invalid request message"
;
else
if
(
cause
instanceof
JsonMappingException
)
{
JsonMappingException
jme
=
(
JsonMappingException
)
cause
;
msg
=
jme
.
getOriginalMessage
();
if
(
jme
.
getPath
()
!=
null
&&
jme
.
getPath
().
size
()
>
0
)
{
msg
=
"Invalid request field: "
+
jme
.
getPath
().
get
(
0
).
getFieldName
()
+
": "
+
msg
;
return
ResponseEntity
.
status
(
HttpStatus
.
BAD_REQUEST
)
.
body
(
new
BaseResponseMessage
(
CODE_ERROR_VALIDATION
,
msg
));
(Where our
BaseResponseMessage
is a POJO with
code
and
message
fields.)