Get Missing Fields When HttpMessageNotReadableException occurs

Viewed 705

I have an API that expects the below dto structure.

data class SignupRequest(
    @NotEmpty(message ="Username must not be empty")
    @NotNull(message = "Username must not be null")
    val username: String,

    @NotEmpty(message ="Password must not be empty")
    @NotNull(message = "Password must not be null")
    val password: String,

    @NotEmpty(message ="Email must not be empty")
    @NotNull(message = "Email must not be null")
    val email: String
)

Below is the controller

@RestController
class AuthenticationController {
   @Autowired
   private lateinit var userRepository: UserRepository

   @PostMapping("/signup")
   @ResponseStatus(HttpStatus.CREATED)
   fun signup(@Valid @RequestBody request: SignupRequest) : SignupResponse {
      val user = User(
            id = UUID.randomUUID(),
            username = request.username,
            password = request.password,
            email = request.email
    )

       try {
          userRepository.save(user)
          return SignupResponse(msg = "Success");
       } catch (e: Exception) {
          throw ResponseStatusException(HttpStatus.BAD_GATEWAY, "Invalid fields", e)
       }
   }
}

When I make an API call if any one of the fields is missing or null I get the HttpMessageNotReadableException, and because the default error message doesn't make much sense to the client I try to format it by providing and error handler for that exception.

@RestControllerAdvice
class ApiExceptionHandler {

   @ExceptionHandler(HttpMessageNotReadableException::class)
   fun handleMessageNotReadableException(
        ex: HttpMessageNotReadableException,
        req: HttpServletRequest,
        res: HttpServletResponse
   ) : ResponseEntity<ApiError> {
       val error = ApiError();
       return ResponseEntity<ApiError>(error, HttpStatus.BAD_REQUEST)
   }

}

However, I also need to know the exact fields that are missing and while the default error message contains this information the exception object doesn't have it, I can't extend ResponseEntityExceptionHandler either because then I have to provide implementations for all the default cases it handles.

How do I easily and correctly return the missing fields in API call

1 Answers

See if the HttpMessageNotReadableException.getCause() is useful. Depending on your underlying JSON library, it might give you what you want. In the case of Jackson JSON parsing, I found that some of the Jackson-specific Exceptions do have field-level information.

For instance,

        Throwable cause = e.getCause();

        if (cause instanceof JsonParseException) {
            JsonParseException jpe = (JsonParseException) cause;
            msg = jpe.getOriginalMessage();
        }

Jackson Javadoc.

I discuss this a bit in a Blog post.

Related