Pengenalan kepada Spring MVC HandlerInterceptor

1. Pengenalan

Dalam tutorial ini kita akan fokus untuk memahami Spring MVC HandlerInterceptor dan cara menggunakannya dengan betul.

2. Penangan MVC Spring

Untuk memahami pemintas, mari kita melangkah mundur dan melihat HandlerMapping . Ini memetakan kaedah ke URL, sehingga DispatcherServlet dapat memintanya ketika memproses permintaan.

Dan DispatcherServlet menggunakan HandlerAdapter untuk benar-benar menggunakan kaedah tersebut.

Sekarang kita memahami keseluruhan konteks - di sinilah pemintas pengendali masuk . Kami akan menggunakan HandlerInterceptor untuk melakukan tindakan sebelum menangani, setelah menangani, atau setelah selesai (ketika tampilan diberikan), permintaan.

Pemintas boleh digunakan untuk masalah silang dan mengelakkan kod pengendali berulang seperti pembalakan, mengubah parameter yang digunakan secara global pada model Spring dll

Dalam beberapa bahagian seterusnya itulah yang akan kita perhatikan - perbezaan antara pelbagai pelaksanaan pencegat.

3. Pergantungan Maven

Untuk menggunakan Interceptors , anda perlu memasukkan bahagian berikut dalam bahagian dependencies pada fail pom.xml anda :

 org.springframework spring-web 5.2.8.RELEASE 

Versi terbaru boleh didapati di sini.

4. Pegangan Spring Handler

Pemintas yang bekerjasama dengan HandlerMapping pada rangka mesti melaksanakan antara muka HandlerInterceptor .

Antara muka ini mengandungi tiga kaedah utama:

  • prehandle () - dipanggil sebelum pengendali sebenar dijalankan, tetapi pandangan belum dihasilkan
  • postHandle () - dipanggil setelah pengendali dijalankan
  • afterCompletion () - dipanggil setelah permintaan lengkap selesai dan pandangan dihasilkan

Ketiga-tiga kaedah ini memberi kelonggaran untuk melakukan semua jenis proses pra dan pasca pemprosesan.

Dan nota ringkas - perbezaan utama antara HandlerInterceptor dan HandlerInterceptorAdapter adalah bahawa pada yang pertama kita perlu mengatasi ketiga-tiga kaedah: preHandle () , postHandle () dan afterCompletion () , sedangkan pada yang kedua kita mungkin hanya melaksanakan kaedah yang diperlukan.

Nota ringkas sebelum kita melangkah lebih jauh - jika anda ingin melangkau teori dan langsung menuju contoh, lompat terus ke bahagian 5.

Inilah rupa pelaksanaan preHandle () yang mudah :

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

Perhatikan kaedah mengembalikan nilai boolean - yang memberitahu Spring jika permintaan tersebut harus diproses lebih lanjut oleh pengendali ( benar ) atau tidak ( salah ).

Seterusnya, kita mempunyai pelaksanaan postHandle () :

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

Kaedah ini dipanggil sebaik sahaja permintaan diproses oleh HandlerAdapter , tetapi sebelum menghasilkan pandangan.

Sudah tentu ia boleh digunakan dalam pelbagai cara - sebagai contoh, kita boleh menambahkan avatar pengguna yang masuk ke dalam model.

Kaedah akhir kita perlu melaksanakan dalam adat HandlerInterceptor pelaksanaan adalah afterCompletion ():

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

Apabila paparan berjaya dihasilkan, kita dapat menggunakan cangkuk ini untuk melakukan perkara seperti mengumpulkan statistik tambahan yang berkaitan dengan permintaan tersebut.

Catatan terakhir yang perlu diingat adalah bahawa HandlerInterceptor didaftarkan ke DefaultAnnotationHandlerMapping bean, yang bertanggungjawab untuk menerapkan pencegat ke mana-mana kelas yang ditandai dengan anotasi @Controller . Selain itu, anda boleh menentukan sebilangan pemintas dalam aplikasi web anda.

5. Pemintas Logger Tersuai

Dalam contoh ini kita akan memfokuskan diri pada log masuk dalam aplikasi web kita. Pertama sekali, kelas kami perlu memperluas HandlerInterceptorAdapter :

public class LoggerInterceptor extends HandlerInterceptorAdapter { ... }

Kami juga perlu mengaktifkan log masuk pemintas kami:

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

Ini memungkinkan Log4J memaparkan log, dan juga menunjukkan, kelas mana yang sedang memasukkan maklumat ke output yang ditentukan.

Seterusnya, mari fokus pada pelaksanaan pencegat khas:

5.1. Kaedah praHandle ()

Kaedah ini dipanggil sebelum menangani permintaan; ia kembali benar, untuk membolehkan rangka untuk menghantar permintaan lebih jauh ke kaedah pengendali (atau ke pemintas seterusnya). Sekiranya kaedah itu dikembalikan salah , Spring menganggap permintaan tersebut telah ditangani dan tidak diperlukan pemprosesan lebih lanjut.

Kita boleh menggunakan cangkuk untuk mencatat maklumat mengenai parameter permintaan: dari mana permintaan itu berasal, dll.

Dalam contoh kami, kami mencatat maklumat ini menggunakan logger Log4J ringkas:

@Override public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("[preHandle][" + request + "]" + "[" + request.getMethod() + "]" + request.getRequestURI() + getParameters(request)); return true; } 

Seperti yang dapat kita lihat, kita mencatat beberapa maklumat asas mengenai permintaan tersebut.

Sekiranya kita menemui kata laluan di sini, kita mesti memastikan bahawa kita tidak mencatatnya.

Pilihan yang mudah adalah menggantikan kata laluan, dan jenis data sensitif yang lain, dengan bintang.

Berikut adalah pelaksanaan cepat bagaimana ia dapat dilakukan:

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.