Spring @ExceptionHandler и HttpMediaTypeNotAcceptableException
У меня есть класс, аннотированный с помощью @ControllerAdvice
, и этот метод в нем:
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ExceptionInfo resourceNotFoundHandler(ResourceNotFoundException ex) {
List<ErrorContent> errors = new ArrayList<>();
errors.add(new ErrorContent(ExceptionsCodes.NOT_FOUND_CODE, null,
"test"));
return fillExceptionInfo(HttpStatus.NOT_FOUND, errors, ex);
}
Вот fillExceptionInfo
:
public ExceptionInfo fillExceptionInfo(HttpStatus status, List<ErrorContent> errors,
Exception ex) {
String msg = ex.getMessage();
return new ExceptionInfo(status.toString(), errors, (msg != null && !msg.equals(""))
? ex.getMessage()
: ExceptionUtils.getFullStackTrace(ex));
}
Когда веб-клиент отправляет запрос на некоторые json-данные, которые не могут быть найдены, этот метод работает нормально. Но когда сервер получает запрос на изображение, вместо моего исключения бросается HttpMediaTypeNotAcceptableException
. Я понимаю, что это происходит из-за неправильного типа содержимого, но как я могу исправить эту проблему?
Обновление
Моя цель - бросить ResourceNotFoundException
в обоих случаях для json-данных и для файла.
Исключение, которое я получаю (поэтому оно выбрано из AbstractMessageConverterMethodProcessor
):
ERROR o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - doResolveHandlerMethodException - Failed to invoke @ExceptionHandler method: public com.lia.utils.GlobalExceptionHandler$ExceptionInfo com.lia.utils.GlobalExceptionHandler.resourceNotFoundHandler(com.lia.app.controllers.exceptions.ResourceNotFoundException)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:168) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:198) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:362) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:60) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:138) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1167) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1004) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) [javax.servlet-api-3.1.0.jar:3.1.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar:3.1.0]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:717) [jetty-servlet-9.1.1.v20140108.jar:9.1.1.v20140108]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1644) [jetty-servlet-9.1.1.v20140108.jar:9.1.1.v20140108]
....
Ответы
Ответ 1
Проблема заключается в несовместимости запрашиваемого типа контента и возвращаемого объекта. См. мой ответ о том, как настроить ContentNegotiationConfigurer
, чтобы Spring определял запрашиваемый тип контента в соответствии с вашими потребностями (смотря на расширение пути, параметр URL или Accept
).
В зависимости от того, как определяется тип запрашиваемого контента, у вас есть следующие параметры, когда клиент запрашивает изображение:
- если запрашиваемый тип содержимого определяется заголовком
Accept
, и если клиент может/хочет обрабатывать ответ JSON вместо данных изображения, клиент должен отправить запрос с помощью Accept: image/*, application/json
. Таким образом, Spring знает, что он может безопасно возвращать либо данные байта изображения, либо сообщение об ошибке JSON.
- в любом другом случае лучшим решением будет просто вернуть код ошибки HTTP без сообщения об ошибке. Вы можете сделать это несколькими способами в своем контроллере:
Установите код ошибки в ответе напрямую
public byte[] getImage(HttpServletResponse resp) {
try {
// return your image
} catch (Exception e) {
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
Используйте ResponseEntity
public ResponseEntity<?> getImage(HttpServletResponse resp) {
try {
byte[] img = // your image
return ReponseEntity.ok(img);
} catch (Exception e) {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Используйте отдельный @ExceptionHandler
метод в этом контроллере, который переопределит обработку исключений по умолчанию Spring. Это предполагает, что у вас есть либо специальный тип исключения для запросов изображения, либо отдельный контроллер для обслуживания изображений. В противном случае обработчик исключений будет обрабатывать исключения из других конечных точек в этом контроллере.
Ответ 2
Как выглядит ваш класс ExceptionInfo
? Я столкнулся с такой же проблемой после определения нескольких обработчиков исключений в классе @ControllerAdvice
аннотированных. Когда произошло исключение, он был пойман, хотя ответ не был возвращен и org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
был брошен.
Я понял, что проблема была вызвана тем, что я пропустил, чтобы добавить методы getter в класс ErrorResponse
. После добавления методов getter (этот класс был неизменным, поэтому не было методов setter), все работало как шарм.