1. Въведение
В този урок ще се съсредоточим върху разбирането на Spring MVC HandlerInterceptor и как да го използваме правилно.
2. Пролетен 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 е регистриран в бин 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:
@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.