InvalidFormatException

InvalidFormatException

我正在使用Spring Boot 1.5.6和Spring Data REST。我正在使用从SDR为我的模型bean之一自动创建的PATCH端点。
我的bean有一些整数字段,我出于某种目的尝试设置字符串值。
我收到的是这样的异常:

    {
  "cause": {
    "cause": null,
    "message": "Can not deserialize value of type int from String \"500s\": not a valid Integer value\n at [Source: N/A; line: -1, column: -1] (through reference chain: it.server.model.checkpoints.CheckPoint[\"passStockAlert\"])"
  },
  "message": "Could not read payload!; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type int from String \"500s\": not a valid Integer value\n at [Source: N/A; line: -1, column: -1] (through reference chain: it.server.model.checkpoints.CheckPoint[\"passStockAlert\"])"
}


我的客户是Angular,我希望客户收到更优美的消息。我的异常通过messages.properties文件进行了本地化,但是在这种情况下,我不能仅显示通用消息。
我应该指出哪个领域是错误的,为什么。

这听起来像是我的验证例外。这是我的豆子:

    @Entity
public class CheckPoint extends AbstractEntity {
    private static final long serialVersionUID = 2719798641638659883L;

    @NotNull(message = "The checkpoint must have a name")
    @Column(nullable = false, unique = true)
    private String name;

    private LocalTime openingTime;

    private LocalTime closingTime;

    @Min(value = 0, message = "The min pass stock alert must be 0")
    @Column(nullable = false)
    private int passStockAlert = 0;


当您尝试在字段passStockAlert中放入小于0的值时,是否有一种方法可以处理这种异常?
确切地说,在这种情况下引发的异常是这样的:

    {
  "errors": [
    {
      "entity": "CheckPoint",
      "property": "passStockAlert",
      "invalidValue": -1,
      "message": "The min pass stock alert must be 0"
    }
  ]
}


====更多澄清=====

目前,我正在使用自定义的异常布局:

    /**
 * According to https://github.com/spring-projects/spring-boot/issues/6555, this
 * is the standard way to customize Spring MVC exceptions.
 *
 * In this case we customized the exception adding localization to the message
 * and adding details about the cause of the error that can be useful for
 * developers.
 *
 * @author Daniele Renda
 *
 */
public class CustomErrorAttributes extends DefaultErrorAttributes {
    private Logger log = LogManager.getLogger();

    @Autowired
    private MessageSource messageSource;

    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Locale locale = LocaleContextHolder.getLocale();
        Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
        Throwable throwable = getError(requestAttributes);

        /**
         * Adding the cause if present
         */
        if (throwable != null && throwable.getCause() != null) {
            Throwable cause = throwable.getCause();
            Map<String, Object> causeErrorAttributes = new HashMap<>();
            causeErrorAttributes.put("exception", cause.getClass().getName());
            causeErrorAttributes.put("message", cause.getMessage());
            errorAttributes.put("cause", causeErrorAttributes);
        }

        if (throwable != null) {
            boolean customizeMessage = false;

            if (throwable instanceof InvalidDataAccessApiUsageException) {
                customizeMessage = true;
            }

            /**
             * Override the messages of these exceptions
             */
            if (customizeMessage) {
                String localizedMessage = localizedMessage(throwable, locale);
                if (localizedMessage != null)
                    errorAttributes.put("message", localizedMessage);
            }
        }
        return errorAttributes;
    }

    private String localizedMessage(Throwable throwable, Locale locale) {
        if (throwable != null)
            return messageSource.getMessage(throwable.getClass().getName(), new Object[] {}, locale);
        return null;
    }

}


我正在使用验证侦听器:

@Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
    validatingListener.addValidator("beforeCreate", validator);
    validatingListener.addValidator("beforeSave", validator);
}


目前,我没有使用任何@RestControllerAdvice,因为我不需要它。到目前为止,一切都得到了很好的管理。

最佳答案

不知道我离目标有多远,但我想还是要提供详细信息。假定您已经具有从DefaultErrorAttributes.getError获取可抛出对象的逻辑,则可以确定其类型是否为InvalidFormatException并进行适当处理。在我的示例中,我执行了以下操作

@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {

    @Autowired
    private MessageSource messageSource;

    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Locale locale = LocaleContextHolder.getLocale();
        Map<String, Object> errorAttributes = new LinkedHashMap<>();
        errorAttributes.put("timestamp", new Date());
        addStatus(errorAttributes, requestAttributes);

        Throwable throwable = getError(requestAttributes);
        if (throwable instanceof BindingResult) {
            addErrors(errorAttributes, (BindingResult) throwable, locale);
        } else if (throwable instanceof MethodArgumentNotValidException) {
            addErrors(errorAttributes, ((MethodArgumentNotValidException) throwable).getBindingResult(), locale);
        } else if (throwable instanceof InvalidFormatException) {
            addErrors(errorAttributes, (InvalidFormatException) throwable, locale);

        }
        return errorAttributes;
    }

    private void addStatus(Map<String, Object> errorAttributes,
            RequestAttributes requestAttributes) {
        Integer status = getAttribute(requestAttributes,
                "javax.servlet.error.status_code");
        if (status == null) {
            errorAttributes.put("status", 999);
            errorAttributes.put("error", "None");
            return;
        }
        errorAttributes.put("status", status);
        try {
            errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
        }
        catch (Exception ex) {
            // Unable to obtain a reason
            errorAttributes.put("error", "Http Status " + status);
        }
    }

    @SuppressWarnings("unchecked")
    private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
        return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
    }

    private void addErrors(
            Map<String, Object> errorAttributes, BindingResult bindingResult, Locale locale) {
        List<ErrorDTO> errors = new ArrayList<>();
        for (ObjectError error : bindingResult.getAllErrors()) {
            ErrorDTO e = new ErrorDTO();
            e.setCode(error.getCode());
            e.setMessage(localizedMessage(error, locale));

            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                e.setField(fieldError.getField());
                e.setRejectedValue(fieldError.getRejectedValue());
            }
            errors.add(e);
        }
        errorAttributes.put("errors", errors);
    }

    private String getInvalidFormatExceptionFieldName(InvalidFormatException ex) {

        for (JsonMappingException.Reference r : ex.getPath()) {
            return r.getFieldName();
        }

        return null;
    }

    private void addErrors(
            Map<String, Object> errorAttributes, InvalidFormatException ex, Locale locale) {
        List<ErrorDTO> errors = new ArrayList<>();

        ErrorDTO e = new ErrorDTO();
        e.setCode("InvalidFormatException");
        String message = localizedMessage(
                "InvalidFormatException",
                new Object[] {ex.getTargetType().getName(), ex.getValue()},
                locale);
        e.setMessage(message);
        e.setField(getInvalidFormatExceptionFieldName(ex));
        e.setRejectedValue(ex.getValue());
        errors.add(e);
        errorAttributes.put("errors", errors);

    }

    private String localizedMessage(ObjectError error, Locale locale) {
        return messageSource.getMessage(error, locale);
    }

    private String localizedMessage(String message, Object[] args, Locale locale) {
        return messageSource.getMessage(message, args, locale);
    }
}


关注InvalidFormatException异常。我将throwable转换为InvalidFormatException,然后允许我同时获取字段名称,类型和值。我不是DefaultErrorAttributes提供的默认映射的粉丝,所以我创建了一个自定义的ErrorDTO,看起来像

public class ErrorDTO {

    private String code;
    private String message;
    private String field;
    private Object rejectedValue;

    public ErrorDTO() {

    }

    public ErrorDTO(String code, String message) {
        this(code, message, null, null);
    }

    public ErrorDTO(String code, String message, String field, Object rejectedValue) {
        this.code = code;
        this.message = message;
        this.field = field;
        this.rejectedValue = rejectedValue;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public Object getRejectedValue() {
        return rejectedValue;
    }

    public void setRejectedValue(Object rejectedValue) {
        this.rejectedValue = rejectedValue;
    }

}


为了获取本地化消息,我传入了一个自定义消息键“ InvalidFormatException”,该键位于ValidationMessages.properties文件中,并定义为

InvalidFormatException={1} is not valid for type {0}


以及目标类型和值(作为对象数组)和messageSource.getMessage的语言环境

这将产生JSON响应

{
   "timestamp":1504310911502,
   "status":999,
   "error":"None",
   "errors":[
      {
         "code":"InvalidFormatException",
         "message":"500s is not valid for type int",
         "field":"passStockAlert",
         "rejectedValue":"500s"
      }
   ]
}

07-26 03:24