Подтвердить что ты не робот

Spring MVC 3.2 - Консолидация содержимого для страниц с ошибками?

Чтобы глобально обрабатывать ошибки (например, HTTP 404), которые могут возникать вне контроллера, у меня есть записи, похожие на следующие в моем web.xml:

<error-page>
    <error-code>404</error-code>
    <location>/errors/404</location>
</error-page>

В моем ErrorController у меня есть соответствующие методы, похожие на следующие:

@Controller
@RequestMapping("/errors")
public class ErrorController {

    @RequestMapping(value = "/404", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<ErrorResponse> error404() {

        ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!");

        return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND);
    }
}

Проблема, с которой я сталкиваюсь, заключается в том, что сконфигурированные ContentNegotiationManager и конвертеры сообщений, которые я сконфигурировал, в этом случае не используются. Я подозреваю, что, поскольку запрос перенаправляется на страницу с ошибкой, исходные атрибуты запроса, используемые в согласовании контента, теряются, и это рассматривается как полностью отдельный запрос. (т.е. исходный запрос для /mycontroller/badresource.json → /errors/404 (без расширения файла))

Есть ли какой-либо способ в обработчике ошибок, подобный этому, определять и/или отвечать соответствующим типом контента, как это запрошено в исходном запросе?

4b9b3361

Ответ 1

Я придумал немного для этого, но, похоже, он работает. В основном это связано с дополнительным переходом в обработке ошибок, чтобы определить расширение файла исходного запроса.

В моем web.xml у меня есть ошибка, перенаправленная на промежуточное действие:

<error-page>
    <error-code>404</error-code>
    <location>/errors/redirect</location>
</error-page>

Затем перед отправкой на действие, которое будет генерировать ответ об ошибке, выполняется проверка, чтобы проверить, существует ли расширение файла в исходном запросе. Если бы это было так, это обеспечило бы его добавление к прямому URI. Заголовки HTTP автоматически пересылаются, поэтому, если согласование содержимого, которое у вас установлено, включает только расширения файлов или HTTP-заголовок, это позволит эффективно разрешить "странице ошибки" возвращать ошибку в соответствующем типе контента.

@Controller
@RequestMapping("/errors")
public class ErrorController {

    @RequestMapping(value = "/redirect", method = RequestMethod.GET)
    public void errorRedirect(HttpServletRequest request, HttpServletResponse response) {

        // Get original request URI
        String uri = (String)request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE);

        // Try to determine file extension
        String filename = WebUtils.extractFullFilenameFromUrlPath(uri);
        String extension = StringUtils.getFilenameExtension(filename);
        extension = StringUtils.hasText(extension) ? "." + extension : "";

        // Forward request to appropriate handler with original request file extension (i.e. /errors/404.json)
        String forwardUri = "/errors/404" + extension); 
        request.getRequestDispatcher(forwardUri).forward(request, response);
    }

    @RequestMapping(value = "/404", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<ErrorResponse> error404() {

        ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!");

        return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND);
    }
}

Ответ 2

Да, действительно, коды ошибок Exceptions и HTTP Response - это две разные вещи.

Вы можете адаптировать код, как показано ниже, чтобы у вас был доступ к requestUri. Я думаю, вы можете найти тип контента на его основе. Я знаю его грубую, но я не думаю, что у нас есть альтернативное решение:

@RequestMapping(value = "/404", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<ErrorResponse> error404(HttpServletRequest request) {

    ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!");

    String requestUri = request.getRequestURI();

    return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND);
}

В примере я предполагаю, что ваше приложение является службой REST, тогда, вероятно, вы можете обратиться к этой ссылке о том, как обрабатывается 404, в REST full обслуживание.

Ответ 3

Spring MVC 3.2 теперь включает полезную аннотацию под названием @ControllerAdvice. Вы можете добавить метод ExceptionHandler, который будет глобально обрабатывать любое исключение, которое вы определили.

Для меня мне только два возможных типа контента возвращаются клиенту - application/json или text/html.

Вот как я его установил -

@ControllerAdvice
public class ExceptionControllerAdvice {

    private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private static final MediaType JSON_MEDIA_TYPE = new MediaType("application", "json", DEFAULT_CHARSET);

    //I decided to handle all exceptions in this one method
    @ExceptionHandler(Throwable.class)
    public @ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException {

        ...

        if(supportsJsonResponse(request.getHeader("Accept"))) {

            //return response as JSON
            response.setStatus(statusCode);
            response.setContentType(JSON_MEDIA_TYPE.toString());

                    //TODO serialize your error in a JSON format
                    //return ...

        } else {

            //return as HTML
            response.setContentType("text/html");
            response.sendError(statusCode, exceptionMessage);
            return null;
        }
    }

    private boolean supportsJsonResponse(String acceptHeader) {

        List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader);

        for(MediaType mediaType : mediaTypes) {
            if(JSON_MEDIA_TYPE.includes(mediaType)) {
                return true;
            }
        }

        return false;
    }

}