Страницы

Поиск по вопросам

пятница, 13 марта 2020 г.

Spring перехват ошибок (404)

#spring_mvc #http_status_code_404


Всем привет. Изучаю Java Web и Spring MVC. Столкнулся с такой проблемой. Нужно сделать
свою страницу ошибок. Я не до конца понял как эти ошибки отлавливать. Понятно как отлавливать
ошибки в контроллере типа ("page/${id}"). Мы ищем инфу по данному id и если такой нет,
выбрасываем исключение или тупо редиректим куда нибудь. Тут всё понятно. Но я хочу,
что бы это всё делалось не в каждом контроллере, а где то в одном месте и не важно
по какому роуту была эта ошибка.
Что то в роде такого. 
Прописать, что для всех исключений (404), мы резолвим такую jsp и передаём в модель
то-то и то-то. И вообще пофиг какой роут вызвал эту ошибку.
В общем как то так.
Заранее благодарен.
    


Ответы

Ответ 1



Spring Web построен на сервлетах. Чтобы наиболее полно ответить на ваш вопрос, давайте посмотрим под DispatcherServlet, который в Spring отвечает за обработку всех запросов. Вот код, который вызывается, если Spring не находит метод контроллера, который должен обрабатывать запрос: protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { if (pageNotFoundLogger.isWarnEnabled()) { pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) + "] in DispatcherServlet with name '" + getServletName() + "'"); } if (this.throwExceptionIfNoHandlerFound) { throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), new ServletServerHttpRequest(request).getHeaders()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); } } Из него видно, что: Пишется сообщение в лог Если установлен флаг throwExceptionIfNoHandlerFound, то бросается исключение NoHandlerFoundException Если флаг throwExceptionIfNoHandlerFound не установлен, то вызывается метод объекта HttpServletResponse sendError(); По умолчанию, флаг сброшен throwExceptionIfNoHandlerFound = false, т.е. вызывается метод sendError(), и как результат контейнер сервлетов переадресует вас на страницу ошибки. Чтобы переопределить эту страницу на собственную, согласно спецификации JEE. вам нужно задать параметр error-page в дескрипторе развертывания web.xml 404 /404 Соответственно, нужно создать и метод контроллера, который будет замапен на URL /404 и вернет нужную страницу. Может случиться так, что вам потребуется выкинуть пользователя на страницу 404 вручную. Глобально это можно сделать через аннотацию @ControllerAdvice, действия перечисленные в классе помеченным данной аннотацией являются дефолтными для всех контроллеров. Создаем собственный класс исключения: public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException() { super(); } } Ловим это исключение глобально для всех контроллеров, и перекидываем на /404 @ControllerAdvice public class ExceptionInterceptor { @ExceptionHandler(ResourceNotFoundException.class) public ModelAndView handleError404(HttpServletRequest request, Exception e) { ModelAndView mv = new ModelAndView("/404"); return mv; } } Соотвественно чтобы выкинуть пользователя на 404 из любого контроллера, просто бросаете это исключение. Передать дополнительный параметры можно просто добавив нужных полей в класс исключения: собственный код ошибки, сообщение и т.д. Теперь когда у нас есть глобальный @ExceptionHandler можно вспомнить про флаг throwExceptionIfNoHandlerFound и сделать так, что 404 будет обрабатываться только в нем. Меняем значение флага: public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override public void customizeRegistration(ServletRegistration.Dynamic registration) { registration.setInitParameter("throwExceptionIfNoHandlerFound", "true"); } } И дописываем в @ExceptionHandler перехват соответствующего класса исключений: @ExceptionHandler(value = {ResourceNotFoundException.class, NoHandlerFoundException.class}) public ModelAndView handleError404(HttpServletRequest request, Exception e) { ModelAndView mv = new ModelAndView("/404"); return mv; } При этом параметры error-page обычно все равно стараются оставлять "на всякий пожарный случай" как fallback, чтобы пользователь вдруг не увидел трекстрейс, который Tomcat показывает в своих сообщениях по дефолту. Полезные ссылки на официальные примеры: error-page Spring error handling

Комментариев нет:

Отправить комментарий