1. Обзор
В этом руководстве мы сосредоточимся на понимании Spring MVC HandlerInterceptor
и на том, как его правильно использовать. ``
2. Spring обработчик MVC
Чтобы понять, как работает перехватчик Spring, давайте сделаем шаг назад и посмотрим на HandlerMapping
.
Целью HandlerMapping
является сопоставление метода обработчика с URL-адресом. Таким образом, DispatcherServlet
сможет вызывать его при обработке запроса.
На самом деле DispatcherServlet
использует HandlerAdapter
для фактического вызова метода.
Короче говоря, перехватчики перехватывают запросы и обрабатывают их. Они помогают избежать повторяющегося кода обработчика, такого как ведение журнала и проверки авторизации.
Теперь, когда мы понимаем общий контекст, давайте посмотрим, как использовать HandlerInterceptor
для выполнения некоторых действий до и после обработки.
3. Зависимости Maven
Чтобы использовать перехватчики, нам нужно включить зависимость spring-web
в наш pom.xml
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.13</version>
</dependency>
4. Перехватчик Spring Handler
Проще говоря, перехватчик Spring — это класс, который либо расширяет класс HandlerInterceptorAdapter
, либо реализует интерфейс HandlerInterceptor
.
HandlerInterceptor содержит
три основных метода:
prehandle()
— вызывается перед выполнением фактического обработчика.postHandle()
– вызывается после выполнения обработчикаafterCompletion()
— вызывается после завершения полного запроса и создания представления.
Эти три метода обеспечивают гибкость для выполнения всех видов предварительной и последующей обработки.
Небольшое примечание перед тем, как мы пойдем дальше: чтобы пропустить теорию и сразу перейти к примерам, перейдите сразу к разделу 5.
Вот простая реализация preHandle()
:
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// your code
return true;
}
Обратите внимание, что метод возвращает логическое
значение. Он сообщает Spring обработать запрос дальше ( true
) или нет ( false
).
Далее у нас есть реализация postHandle()
:
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// your code
}
Перехватчик вызывает этот метод сразу после обработки запроса, но до создания представления.
Например, мы можем использовать этот метод, чтобы добавить в модель аватар вошедшего в систему пользователя.
Последний метод, который нам нужно реализовать, это afterCompletion()
:
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
// your code
}
Этот `` метод позволяет нам выполнять пользовательскую логику после завершения обработки запроса.
Кроме того, стоит отметить, что мы можем зарегистрировать несколько пользовательских перехватчиков. Для этого мы можем использовать DefaultAnnotationHandlerMapping
.
5. Пользовательский перехватчик регистратора
В этом примере мы сосредоточимся на регистрации в нашем веб-приложении.
Во-первых, нашему классу нужно реализовать HandlerInterceptor
:
public class LoggerInterceptor implements HandlerInterceptor {
...
}
Нам также нужно включить ведение журнала в нашем перехватчике:
private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);
Это позволяет Log4J
отображать журналы, а также указывать, какой класс в настоящее время записывает информацию в указанный выход.
Далее, давайте сосредоточимся на наших собственных реализациях перехватчиков.
5.1. Метод preHandle()
Как следует из названия, перехватчик вызывает preHandle()
перед обработкой запроса.
По умолчанию этот метод возвращает значение true
, чтобы отправить запрос дальше методу-обработчику. Однако мы можем сказать Spring остановить выполнение, вернув false
.
Мы можем использовать хук для регистрации информации о параметрах запроса, например, откуда пришел запрос.
В нашем примере мы регистрируем эту информацию с помощью простого регистратора Log4J
:
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("[preHandle][" + request + "]" + "[" + request.getMethod()
+ "]" + request.getRequestURI() + getParameters(request));
return true;
}
Как мы видим, мы записываем основную информацию о запросе.
В случае, если мы столкнемся здесь с паролем, нам нужно убедиться, что мы не зарегистрируем его, конечно. Простым вариантом было бы заменить пароли и любые другие конфиденциальные данные звездочками.
Вот краткая реализация того, как это сделать:
private String getParameters(HttpServletRequest request) {
StringBuffer posted = new StringBuffer();
Enumeration<?> e = request.getParameterNames();
if (e != null) {
posted.append("?");
}
while (e.hasMoreElements()) {
if (posted.length() > 1) {
posted.append("&");
}
String curr = (String) e.nextElement();
posted.append(curr + "=");
if (curr.contains("password")
|| curr.contains("pass")
|| curr.contains("pwd")) {
posted.append("*****");
} else {
posted.append(request.getParameter(curr));
}
}
String ip = request.getHeader("X-FORWARDED-FOR");
String ipAddr = (ip == null) ? getRemoteAddr(request) : ip;
if (ipAddr!=null && !ipAddr.equals("")) {
posted.append("&_psip=" + ipAddr);
}
return posted.toString();
}
Наконец, мы стремимся получить исходный IP-адрес HTTP-запроса.
Вот простая реализация:
private String getRemoteAddr(HttpServletRequest request) {
String ipFromHeader = request.getHeader("X-FORWARDED-FOR");
if (ipFromHeader != null && ipFromHeader.length() > 0) {
log.debug("ip from proxy - X-FORWARDED-FOR : " + ipFromHeader);
return ipFromHeader;
}
return request.getRemoteAddr();
}
5.2. postHandle()
Метод
Перехватчик вызывает этот метод после выполнения обработчика, но до того, как DispatcherServlet
отобразит представление.
Мы можем использовать его для добавления дополнительных атрибутов в ModelAndView
. Другим вариантом использования может быть вычисление времени обработки запроса.
В нашем случае мы просто зарегистрируем наш запрос непосредственно перед тем, как DispatcherServlet
отобразит представление:
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info("[postHandle][" + request + "]");
}
5.3. метод afterCompletion()
Мы можем использовать этот метод для получения данных запроса и ответа после рендеринга представления:
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (ex != null){
ex.printStackTrace();
}
log.info("[afterCompletion][" + request + "][exception: " + ex + "]");
}
6. Конфигурация
Теперь, когда мы собрали все части вместе, давайте добавим наш собственный перехватчик.
Для этого нам нужно переопределить метод addInterceptors()
:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
}
Мы можем добиться той же конфигурации, отредактировав наш файл конфигурации XML Spring:
<mvc:interceptors>
<bean id="loggerInterceptor" class="com.foreach.web.interceptor.LoggerInterceptor"/>
</mvc:interceptors>
Если эта конфигурация активна, перехватчик будет активен, и все запросы в приложении будут правильно регистрироваться.
Обратите внимание, что если настроено несколько перехватчиков Spring, метод preHandle()
выполняется в порядке конфигурации, тогда как методы postHandle()
и afterCompletion()
вызываются в обратном порядке.
Имейте в виду, что нам не нужно аннотировать наш класс конфигурации с помощью @EnableWebMvc
, если мы используем Spring Boot вместо vanilla Spring.
7. Заключение
В этой статье представлено краткое введение в перехват HTTP-запросов с использованием Spring MVC Handler Interceptors.
Все примеры и конфигурации доступны на GitHub .