Перейти к основному содержимому

Введение в Spring MVC HandlerInterceptor

· 6 мин. чтения

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 .