Всем привет. Изучаю 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
Соответственно, нужно создать и метод контроллера, который будет замапен на 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
Комментариев нет:
Отправить комментарий