Страницы

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

пятница, 21 июня 2019 г.

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

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


Ответ

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

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

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