Вот проблема: у меня есть контроллер, который принимает модель ввода. Скажем так
public class AppUserUpdateData {
@NotNull
@Size(min = 1, max = 50)
protected String login;
@JsonDeserialize(using = MyDateTimeDeserializer.class)
protected Date startWorkDate;
*************
other properties and methods
*************
}
Проблема в том, что когда я хочу ограничить доску даты, я в конечном итоге получаю исключение HTTP 400 без каких-либо сообщений, несмотря на то, что я обрабатываю этот случай в своем коде! вот контроллер:
@RequestMapping(
value = "/users/{userId}", method = RequestMethod.PUT,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public @ResponseBody AbstractSuccessResult updateUser(@PathVariable Long userId,
@RequestBody AppUserUpdateData appUserUpdateRequest, HttpServletRequest request) {
AbstractSuccessResult response = new AbstractSuccessResult();
appUserService.updateUser(appUserUpdateRequest, userId);
return response;
}
Вот десериализатор:
public class MyDateTimeDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
try {
return DataTypeHelper.stringToDateTime(jsonParser.getText());
} catch (MyOwnWrittenException ex) {
throw ex;
}
}
}
В DataTypeHelper.stringToDateTime есть несколько проверок, которые блокируют недопустимые строки даты.
И для моего исключения есть обработчик:
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler({ MyOwnWrittenException .class})
protected ResponseEntity<Object> handleInvalidRequest(RuntimeException exc,
WebRequest request) {
MyOwnWrittenException ex = (MyOwnWrittenException) exc;
BasicErrorMessage message; = new BasicErrorMessage(ex.getMessage());
AbstractUnsuccessfulResult result = new AbstractUnsuccessfulResult(message);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return handleExceptionInternal(exc, result, headers, HttpStatus.BAD_REQUEST, request);
}
}
Проблема в том, что когда было выброшено исключение в MyDateTimeDeserializer, оно не попадает в MyExceptionHandler, но я не могу понять почему? Что я делаю неправильно?
В ответ просто пустой ответ с кодом 400 (
UPD Благодаря ответу @Joe Doe проблема была решена. Вот мой обновленный обработчик:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler({ MyOwnWrittenException .class})
protected ResponseEntity<Object> handleInvalidRequest(RuntimeException exc,
WebRequest request) {
MyOwnWrittenException ex = (MyOwnWrittenException) exc;
BasicErrorMessage message; = new BasicErrorMessage(ex.getMessage());
AbstractUnsuccessfulResult result = new AbstractUnsuccessfulResult(message);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return handleExceptionInternal(exc, result, headers, HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
Throwable cause = ex.getCause();
String message = null;
if (cause instanceof JsonMappingException) {
if (cause.getCause() instanceof MyOwnWrittenException) {
return handleInvalidRequest((RuntimeException) cause.getCause(), request);
} else {
message = cause.getMessage();
}
} else {
message = ex.getMessage();
}
AbstractUnsuccessfulResult result = new AbstractUnsuccessfulResult(
new BasicErrorMessage(message));
headers.setContentType(MediaType.APPLICATION_JSON);
return handleExceptionInternal(ex, result, headers, HttpStatus.BAD_REQUEST, request);
}
}
UPD
В моем проекте без аннотации @Order(Ordered.HIGHEST_PRECEDENCE) не работает
Я считаю, что это из-за количества ControllerAdvices в проекте.




Прежде чем updateUser в вашем контроллере будет вызван, его аргументы должны быть разрешены. Здесь на помощь приходит HandlerMethodArgumentResolverComposite, который делегирует одну из предварительно зарегистрированных HandlerMethodArgumentResolver - в данном конкретном случае он делегирует RequestResponseBodyMethodProcessor.
Под делегированием я подразумеваю вызов метода распознавателя resolveArgument. Этот метод косвенно вызывает метод deserialize из десериализатора, который вызывает исключение типа MyOwnWrittenException. Проблема в том, что это исключение оборачивается другим исключением. Фактически, к тому времени, когда он вернется к resolveArgument, он будет иметь тип HttpMessageNotReadableException.
Таким образом, вместо того, чтобы перехватывать MyOwnWrittenException в вашем пользовательском обработчике исключений, вам нужно перехватывать исключения типа HttpMessageNotReadableException. Затем в методе, который обрабатывает этот случай, вы можете проверить, действительно ли «исходное» исключение было MyOwnWrittenException - вы можете сделать это, неоднократно вызывая метод getCause. В моем случае (вероятно, будет то же самое в вашем) мне нужно было дважды вызвать getCause, чтобы «развернуть» исходное исключение (HttpMessageNotReadableException -> JsonMappingException -> MyOwnWrittenException).
Обратите внимание, что вы не можете просто заменить MyOwnWrittenException на HttpMessageNotReadableException в своем обработчике исключений, поскольку он конфликтует (во время выполнения) с другим методом, специально разработанным для обработки исключений последнего типа, который называется handleHttpMessageNotReadable.
Таким образом, вы можете сделать что-то вроде этого:
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// ex.getCause().getCause().getClass() gives MyOwnWrittenException
// the actual logic that handles the exception...
}
}
Да, помогло! Большое спасибо!
Работает! Я признаю:)
Не могли бы вы также выложить
MyOwnWrittenException,BasicErrorMessageиAbstractUnsuccessfulResult? Кроме того, я думаю, что у вас есть слишком сложные вещи, и в этом нет особой необходимости, но, пожалуйста, опубликуйте выше, чтобы проверить свой код.