All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.naharoo.commons.mstoolkit.rest.exceptionhandler.HttpMessageNotReadableExceptionHandler Maven / Gradle / Ivy

There is a newer version: 0.7.12
Show newest version
package com.naharoo.commons.mstoolkit.rest.exceptionhandler;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.naharoo.commons.mstoolkit.exceptions.CommonIssueType;
import com.naharoo.commons.mstoolkit.exceptions.IssueType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
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;
import java.time.LocalDateTime;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.String.format;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static java.util.Objects.isNull;
import static java.util.regex.Pattern.compile;

@ControllerAdvice
@ConditionalOnProperty(prefix = "ms-toolkit.rest-exception-handler.handlers.enabled", name = "NOT_READABLE_REQUEST_BODY", havingValue = "true", matchIfMissing = true)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(name = "org.springframework.http.converter.HttpMessageNotReadableException")
public class HttpMessageNotReadableExceptionHandler extends AbstractExceptionHandler {

  public static final String ENUM_FAIL_ON_NUMBERS_MSG = "Numeric value for Enum not supported.";
  private static final Pattern ENUM_MSG_PATTERN =
          compile("(.*)\\.(.*)` from String \"(.*)(\": not one of the values accepted for Enum class: )(.*)");
  private static final String ENUM_ERROR_MSG = "%s: invalid value '%s' should be one of %s";
  private static final String ERROR_MSG = "%s: %s";
  private static final Pattern ENUM_FAIL_ON_NUMBERS_PATTERN = compile(
          "^.*FAIL_ON_NUMBERS_FOR_ENUMS.*$",
          Pattern.DOTALL
  );

  /**
   * Handle HttpMessageNotReadableException. Thrown when request body is malformed.
   */
  @ExceptionHandler(HttpMessageNotReadableException.class)
  public ResponseEntity handle(
          final HttpMessageNotReadableException exception,
          final HttpServletRequest request
  ) {
    final HttpServletRequestInfoBuilder infoBuilder = HttpServletRequestInfoBuilder.newInstance(
            request);
    logTrace(exception, infoBuilder);

    final IssueType type = CommonIssueType.NOT_READABLE_REQUEST_BODY;
    final int statusCode = type.statusCode();

    final ResponseEntity response = ResponseEntity
            .status(statusCode)
            .body(new ApiErrorResponse(
                    statusCode,
                    singleton(type),
                    singletonList(buildErrorMessage(exception)),
                    LocalDateTime.now()
            ));

    logInfo(exception, infoBuilder);
    return response;
  }

  private String buildErrorMessage(final HttpMessageNotReadableException exception) {
    final Throwable cause = exception.getCause();
    if (isNull(cause)) {
      return exception.getLocalizedMessage();
    }

    if (cause instanceof InvalidFormatException) {
      return this.buildInvalidFormatExceptionMessage((InvalidFormatException) cause);
    } else if (cause instanceof JsonProcessingException) {
      return ((JsonProcessingException) cause).getOriginalMessage();
    } else {
      return exception.getLocalizedMessage();
    }
  }

  private String buildInvalidFormatExceptionMessage(
          final InvalidFormatException invalidFormatException
  ) {
    String errorMessage = null;
    final String path = buildPath(invalidFormatException.getPath());
    final String invalidFormatExceptionMessage = invalidFormatException.getMessage();
    if (!isNull(invalidFormatExceptionMessage)) {
      if (ENUM_FAIL_ON_NUMBERS_PATTERN.matcher(invalidFormatExceptionMessage).matches()) {
        return format(ERROR_MSG, path, ENUM_FAIL_ON_NUMBERS_MSG);
      }

      final String[] exceptionMessageLines = invalidFormatExceptionMessage.split("\n");
      if (exceptionMessageLines.length > 1) {
        final Matcher enumErrorMsgMatcher = ENUM_MSG_PATTERN.matcher(exceptionMessageLines[0]);

        if (enumErrorMsgMatcher.matches()) {
          final String invalidValue = enumErrorMsgMatcher.group(3);
          final String possibleValues = enumErrorMsgMatcher.group(5);
          errorMessage = format(ENUM_ERROR_MSG, path, invalidValue, possibleValues);
        } else {
          errorMessage = format(ERROR_MSG, path, exceptionMessageLines[0]);
        }
      }
    }
    return errorMessage;
  }

  private String buildPath(final List pathReference) {
    final StringBuilder path = new StringBuilder();
    pathReference.forEach(
            reference -> {
              if (reference.getIndex() > -1) {
                path.append("[").append(reference.getIndex()).append("]");
              } else {
                if (path.length() > 0) {
                  path.append(".");
                }
                path.append(reference.getFieldName());
              }
            });
    return path.toString();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy