1. Введение
В этом руководстве мы сосредоточимся на понимании Spring MVC HandlerInterceptor и на том, как его правильно использовать.
2. Обработчик Spring MVC
Чтобы понять перехватчик, давайте сделаем шаг назад и посмотрим на HandlerMapping . Это сопоставляет метод с URL-адресом, так что DispatcherServlet может вызывать его при обработке запроса.
А DispatcherServlet использует HandlerAdapter для фактического вызова метода.
Теперь, когда мы понимаем общий контекст - здесь на помощь приходит обработчик-перехватчик . Мы будем использовать HandlerInterceptor для выполнения действий перед обработкой, после обработки или после завершения (когда представление визуализируется) запроса.
Перехватчик может использоваться для сквозных задач и для избежания повторяющегося кода обработчика, такого как ведение журнала, изменение глобально используемых параметров в модели Spring и т. Д.
В следующих нескольких разделах мы будем рассматривать именно это - различия между различными реализациями перехватчиков.
3. Зависимости Maven
Чтобы использовать перехватчики , вам необходимо включить следующий раздел в раздел зависимостей вашего файла pom.xml :
org.springframework spring-web 5.2.8.RELEASE
Последнюю версию можно найти здесь.
4. Пружинный перехватчик.
Перехватчики, работающие с HandlerMapping на платформе, должны реализовывать интерфейс HandlerInterceptor .
Этот интерфейс содержит три основных метода:
- prehandle () - вызывается перед выполнением фактического обработчика, но представление еще не создано
- postHandle () - вызывается после выполнения обработчика
- afterCompletion () - вызывается после завершения полного запроса и создания представления
Эти три метода обеспечивают гибкость для выполнения всех видов предварительной и последующей обработки.
И небольшое примечание - основное различие между HandlerInterceptor и HandlerInterceptorAdapter заключается в том, что в первом нам нужно переопределить все три метода: 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 }
Этот метод вызывается сразу после обработки запроса 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 ()
Этот метод вызывается перед обработкой запроса; он возвращает истину, чтобы позволить платформе послать запрос далее методу обработчика (или следующему перехватчику). Если метод возвращает false , Spring предполагает, что запрос был обработан и дальнейшая обработка не требуется.
Мы можем использовать ловушку для регистрации информации о параметрах запросов: откуда пришел запрос и т. Д.
В нашем примере мы регистрируем эту информацию с помощью простого логгера 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(); }
Finally, we're aiming to get the source IP address of the HTTP request.
Here's a simple implementation:
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. Method postHandle()
This hook runs when the HandlerAdapter is invoked the handler but DispatcherServlet is yet to render the view.
We can use this method to add additional attributes to the ModelAndView or to determine the time taken by handler method to process a client's request.
In our case, we simply log a request just before DispatcherServlet is going to render a view.
@Override public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("[postHandle][" + request + "]"); }
5.3. Method afterCompletion()
When a request is finished and the view is rendered, we may obtain request and response data, as well as information about exceptions, if any occurred:
@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. Configuration
To add our interceptors into Spring configuration, we need to override addInterceptors() method inside WebConfig class that implements WebMvcConfigurer:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoggerInterceptor()); }
We may achieve the same configuration by editing our XML Spring configuration file:
With this configuration active, the interceptor will be active and all requests in the application will be properly logged.
Please notice, if multiple Spring interceptors are configured, the preHandle() method is executed in the order of configuration, whereas postHandle() and afterCompletion() methods are invoked in the reverse order.
If we're using Spring Boot instead of vanilla Spring, we should keep in mind to not annotate our configuration class with @EnableWebMvc, or we'll lose out on Boot's auto configurations.
7. Conclusion
This tutorial is a quick introduction to intercepting HTTP requests using Spring MVC Handler Interceptor.
All examples and configurations are available here on GitHub.