I have requirements that if the JSON for the post request is invalid, I will need to send 400 HTTP response codes and if any fields are not parsable, the return status code will be 422. An example post request can be:
{ "amount": "12.3343", "timestamp": "2018-07-17T09:59:51.312Z" } The Dto class is provided as below,
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class TransactionDto { @NotNull @Min(0) private BigDecimal amount; @NotNull private LocalDateTime timestamp; } This is the controller with POST request,
@Slf4j @RestController @RequestMapping("/") @Validated public class TransactionController { @Autowired private TransactionService transactionService; @Operation(description = "create a transaction using the provided JSON data") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Create transaction", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Transaction.class))}), @ApiResponse(responseCode = "204", description = "Transaction is older than 60 seconds", content = @Content(mediaType = "application/json")), @ApiResponse(responseCode = "500", description = MessageConstant.INTERNAL_SERVER_ERROR_MSG, content = @Content)}) @PostMapping(value = "/transactions") public ResponseEntity<Object> createProperty(@RequestBody @Valid TransactionDto transactionDto) { try { final LocalDateTime transactionTimestamp = transactionDto.getTimestamp(); final LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneOffset.UTC); final long secondsDuration = Duration.between(transactionTimestamp, localDateTimeNow).toSeconds(); final boolean isFuture = transactionTimestamp.isAfter(localDateTimeNow); if (isFuture) { return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.UNPROCESSABLE_ENTITY, "Transaction is in future"), new HttpHeaders(), HttpStatus.ACCEPTED); } if (secondsDuration > 60) { return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.NO_CONTENT, "Transaction is older than 60 seconds"), new HttpHeaders(), HttpStatus.NO_CONTENT); } final Transaction transaction = transactionService.createTransaction(transactionDto); return new ResponseEntity<>(transaction, new HttpHeaders(), HttpStatus.CREATED); } catch (Exception ex) { log.error(MessageConstant.INTERNAL_SERVER_ERROR_MSG + ex.getMessage()); return new ResponseEntity<>(ApiResponseMessage.getInternalServerError(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR); } } } If the "amount" is, say, "sfdfd", this is not BigDecimal, we should provide the 422. But if the "amount" is "-12.3343", this is a constraint validation error but the data is valid and parsable. So we can't have the 422.
This is my exception handling class,
@Slf4j @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @Override @Nonnull protected ResponseEntity<Object> handleMissingServletRequestParameter( MissingServletRequestParameterException ex, @Nonnull HttpHeaders headers, @Nonnull HttpStatus status, @Nonnull WebRequest request ) { String error = ex.getParameterName() + " parameter is missing"; return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST, error), new HttpHeaders(), HttpStatus.BAD_REQUEST); } @Override @Nonnull protected ResponseEntity<Object> handleHttpMediaTypeNotSupported( @Nonnull HttpMediaTypeNotSupportedException ex, @Nonnull HttpHeaders headers, @Nonnull HttpStatus status, @Nonnull WebRequest request ) { String message = prepareMessageFromException(ex, (ServletWebRequest) request); return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.UNSUPPORTED_MEDIA_TYPE, " media type is not supported. Supported media types "), new HttpHeaders(), HttpStatus.UNSUPPORTED_MEDIA_TYPE); } @Override @NonNull protected ResponseEntity<Object> handleMethodArgumentNotValid( @NonNull MethodArgumentNotValidException ex, @NonNull HttpHeaders headers, @NonNull HttpStatus status, @NonNull WebRequest request ) { String message = prepareMessageFromException(ex, (ServletWebRequest) request); ApiErrorResponse apiError = new ApiErrorResponse(BAD_REQUEST); apiError.setMessage("Validation error"); apiError.addValidationErrors(ex.getBindingResult().getFieldErrors()); apiError.addValidationError(ex.getBindingResult().getGlobalErrors()); return buildResponseEntity(apiError); } @ExceptionHandler(ConstraintViolationException.class) protected ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex) { ApiErrorResponse apiError = new ApiErrorResponse(BAD_REQUEST); apiError.setMessage("Validation error"); apiError.addValidationErrors(ex.getConstraintViolations()); return buildResponseEntity(apiError); } @ExceptionHandler(Exception.class) @ResponseBody public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) { ServletWebRequest req = (ServletWebRequest) request; String message = prepareMessageFromException(ex, (ServletWebRequest) request); log.info("{} to {}", req.getHttpMethod(), req.getRequest().getServletPath()); log.error(message, ex); return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST, message), new HttpHeaders(), HttpStatus.BAD_REQUEST); } @Override @Nonnull protected ResponseEntity<Object> handleHttpMessageNotReadable( @Nonnull HttpMessageNotReadableException ex, @Nonnull HttpHeaders headers, @Nonnull HttpStatus status, @Nonnull WebRequest request ) { String message = prepareMessageFromException(ex, (ServletWebRequest) request); return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, BAD_REQUEST, message), new HttpHeaders(), HttpStatus.BAD_REQUEST); } private String prepareMessageFromException(Exception ex, ServletWebRequest request) { String message = ex.getMessage(); log.info("{} to {}", request.getHttpMethod(), request.getRequest().getServletPath()); log.error(message); if (message != null && !message.isEmpty()) { message = message.split(":")[0]; } return message; } @Override @Nonnull protected ResponseEntity<Object> handleHttpMessageNotWritable( @Nonnull HttpMessageNotWritableException ex, @Nonnull HttpHeaders headers, @Nonnull HttpStatus status, @Nonnull WebRequest request ) { String error = "Error writing JSON output"; return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error. please contact support !!"), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR); } @Override @Nonnull protected ResponseEntity<Object> handleNoHandlerFoundException( NoHandlerFoundException ex, @Nonnull HttpHeaders headers, @Nonnull HttpStatus status, @Nonnull WebRequest request ) { return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST, String.format("Could not find the %s method for URL %s", ex.getHttpMethod(), ex.getRequestURL())), new HttpHeaders(), HttpStatus.BAD_REQUEST); } private ResponseEntity<Object> buildResponseEntity(ApiErrorResponse apiError) { return new ResponseEntity<>(apiError, apiError.getStatus()); } @ExceptionHandler(EntityNotFoundException.class) protected ResponseEntity<Object> handleEntityNotFound(EntityNotFoundException ex) { return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.NOT_FOUND, "Resource not found: "), new HttpHeaders(), HttpStatus.NOT_FOUND); } @ExceptionHandler(DataIntegrityViolationException.class) protected ResponseEntity<Object> handleDataIntegrityViolation(DataIntegrityViolationException ex, WebRequest request) { if (ex.getCause() instanceof ConstraintViolationException) { return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.CONFLICT, "Database error"), new HttpHeaders(), HttpStatus.CONFLICT); } return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error. please contact support !!" + ex.getMessage()), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(MethodArgumentTypeMismatchException.class) protected ResponseEntity<Object> handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException ex, WebRequest request) { return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST, String.format("The parameter '%s' of value '%s' could not be converted to type '%s'", ex.getName(), ex.getValue(), ex.getRequiredType())), new HttpHeaders(), HttpStatus.BAD_REQUEST); } } At the moment, I get 400 in both of the cases mentioned. How do I refactor the code to get the correct response code?
HttpStatus.BAD_REQUESTis going to be 400 I believe