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

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

1. Вступление

В этом руководстве мы сосредоточимся на понимании Spring MVCHandlerInterceptor и на том, как правильно его использовать. __

2. Spring MVC Handler

А чтобы понять перехватчик, давайте сделаем шаг назад и посмотрим наHandlerMapping. Это сопоставляет метод с URL-адресом, чтобыDispatcherServlet мог вызывать его при обработке запроса.

ИDispatcherServlet используетHandlerAdapter для фактического вызова метода.

Теперь, когда мы понимаем общий контекст -this is where the handler interceptor comes in. Мы будем использоватьHandlerInterceptor для выполнения действий перед обработкой, после обработки или после завершения (когда представление визуализируется) запроса.

Перехватчик может использоваться для сквозных задач и во избежание повторяющегося кода обработчика, такого как: ведение журнала, изменение глобально используемых параметров в модели Spring и т. Д.

В следующих нескольких разделах мы и будем рассматривать именно это - различия между различными реализациями перехватчиков.

3. Maven Зависимости

Чтобы использоватьInterceptors, вам необходимо включить следующий раздел в разделdependencies вашего файлаpom.xml:


    org.springframework
    spring-web
    5.0.6.RELEASE

Последнюю версию можно найтиhere.

4. Spring Handler Interceptor

Перехватчики, работающие сHandlerMapping на платформе, должны реализовывать интерфейсHandlerInterceptor.

Этот интерфейс содержит три основных метода:

  • prehandle() - вызывается перед выполнением фактического обработчика, но представление еще не создано

  • postHandle() - вызываетсяafter, обработчик выполняется

  • afterCompletion() – называетсяafter the complete request has finished and view was generated

Эти три метода обеспечивают гибкость при выполнении всех видов предварительной и последующей обработки.

И небольшое примечание - основное различие междуHandlerInterceptor иHandlerInterceptorAdapter заключается в том, что в первом нам нужно переопределить все три метода:preHandle(),postHandle() иafterCompletion(), а во втором мы можем реализовать только необходимые методы.

Небольшое замечание, прежде чем мы двинемся дальше - если вы хотите пропустить теорию и сразу перейти к примерам, сразу переходите к разделу 5.

Вот как будет выглядеть простая реализацияpreHandle():

@Override
public boolean preHandle(
  HttpServletRequest request,
  HttpServletResponse response,
  Object handler) throws Exception {
    // your code
    return true;
}

Обратите внимание, что метод возвращает значениеboolean, которое сообщает Spring, должен ли запрос обрабатываться обработчиком (true) или нет (false).

Далее у нас есть реализацияpostHandle():

@Override
public void postHandle(
  HttpServletRequest request,
  HttpServletResponse response,
  Object handler,
  ModelAndView modelAndView) throws Exception {
    // your code
}

Этот метод вызывается сразу после обработки запросаHandlerAdapter, но перед генерацией представления.

И, конечно, его можно использовать по-разному - например, мы можем добавить аватар зарегистрированного пользователя в модель.

Последний метод, который нам нужно реализовать в пользовательской реализацииHandlerInterceptor, - этоafterCompletion():

@Override
public void afterCompletion(
  HttpServletRequest request,
  HttpServletResponse response,
  Object handler, Exception ex) {
    // your code
}

Когда представление успешно сгенерировано, мы можем использовать этот хук, чтобы собирать дополнительную статистику, связанную с запросом.

Последнее замечание, которое следует запомнить, это то, чтоHandlerInterceptor зарегистрирован в bean-компонентеDefaultAnnotationHandlerMapping, который отвечает за применение перехватчиков к любому классу, отмеченному аннотацией@Controller. Кроме того, вы можете указать любое количество перехватчиков в вашем веб-приложении.

5. Пользовательский перехватчик регистратора

В этом примере мы сосредоточимся на входе в наше веб-приложение. Прежде всего, наш класс должен расширитьHandlerInterceptorAdapter:

public class LoggerInterceptor extends HandlerInterceptorAdapter {
    ...
}

Нам также нужно включить регистрацию в нашем перехватчике:

private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);

Это позволяет Log4J отображать журналы, а также указывать, какой класс в настоящее время записывает информацию в указанный вывод.

Затем давайте сосредоточимся на пользовательских реализациях перехватчика:

5.1. МетодpreHandle()

Этот метод вызывается перед обработкой запроса; он возвращаетtrue,, чтобы позволить фреймворку отправить запрос далее методу обработчика (или следующему перехватчику). Если метод возвращаетfalse, Spring предполагает, что запрос был обработан и дальнейшая обработка не требуется.

Мы можем использовать ловушку для регистрации информации о параметрах запросов: откуда поступил запрос и т. Д.

В нашем примере мы регистрируем эту информацию, используя простой Log4J logger:

@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()

Этот хук запускается, когдаHandlerAdapter вызывает обработчик, но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. конфигурация

Чтобы добавить наши перехватчики в конфигурацию Spring, нам нужно переопределить методaddInterceptors() внутри классаWebConfig, который реализуетWebMvcConfigurer:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoggerInterceptor());
}

Мы можем добиться той же конфигурации, отредактировав наш файл конфигурации XML Spring:


    

Если эта конфигурация активна, перехватчик будет активен, и все запросы в приложении будут правильно зарегистрированы.

Обратите внимание: если настроено несколько перехватчиков Spring, методpreHandle() выполняется в порядке конфигурации, тогда как методыpostHandle() иafterCompletion() вызываются в обратном порядке.

7. Заключение

Это руководство представляет собой краткое введение в перехват HTTP-запросов с помощью Spring MVC Handler Interceptor.

Все примеры и конфигурации доступны здесь наGitHub.