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), все работало как шарм.