Pengantar Spring MVC HandlerInterceptor

1. Perkenalan

Dalam tutorial ini kita akan fokus pada pemahaman Spring MVC HandlerInterceptor dan bagaimana menggunakannya dengan benar.

2. Penangan MVC Musim Semi

Untuk memahami interseptor, mari mundur selangkah dan lihat HandlerMapping . Ini memetakan metode ke URL, sehingga DispatcherServlet akan dapat memanggilnya saat memproses permintaan.

Dan DispatcherServlet menggunakan HandlerAdapter untuk menjalankan metode tersebut.

Sekarang setelah kita memahami konteks keseluruhan - di sinilah interceptor pawang masuk . Kita akan menggunakan HandlerInterceptor untuk melakukan tindakan sebelum menangani, setelah menangani, atau setelah selesai (saat tampilan dirender), dari sebuah permintaan.

Pencegat dapat digunakan untuk masalah lintas sektoral dan untuk menghindari kode penangan berulang seperti logging, mengubah parameter yang digunakan secara global dalam model Spring, dll.

Pada beberapa bagian selanjutnya itulah yang akan kita lihat - perbedaan antara berbagai implementasi interseptor.

3. Ketergantungan Maven

Untuk menggunakan Interceptors , Anda perlu menyertakan bagian berikut di bagian dependensi file pom.xml Anda :

 org.springframework spring-web 5.2.8.RELEASE 

Versi terbaru dapat ditemukan di sini.

4. Interceptor Pegas Pegas

Interceptors yang bekerja dengan HandlerMapping pada framework harus mengimplementasikan antarmuka HandlerInterceptor .

Antarmuka ini berisi tiga metode utama:

  • prehandle () - dipanggil sebelum handler aktual dijalankan, tetapi tampilan belum dibuat
  • postHandle () - dipanggil setelah handler dijalankan
  • afterCompletion () - dipanggil setelah permintaan lengkap selesai dan tampilan dibuat

Ketiga metode ini memberikan fleksibilitas untuk melakukan semua jenis pemrosesan sebelum dan sesudah.

Dan catatan singkat - perbedaan utama antara HandlerInterceptor dan HandlerInterceptorAdapter adalah di metode pertama kita perlu mengganti ketiga metode: preHandle () , postHandle (), dan afterCompletion () , sedangkan di metode kedua kita mungkin hanya mengimplementasikan metode yang diperlukan.

Catatan singkat sebelum kita melangkah lebih jauh - jika Anda ingin melewatkan teori dan langsung ke contoh, langsung ke bagian 5.

Beginilah implementasi preHandle () sederhana akan terlihat:

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

Perhatikan bahwa metode mengembalikan nilai boolean - yang memberi tahu Spring apakah permintaan harus diproses lebih lanjut oleh penangan ( true ) atau tidak ( false ).

Selanjutnya, kami memiliki implementasi postHandle () :

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

Metode ini dipanggil segera setelah permintaan diproses oleh HandlerAdapter , tetapi sebelum membuat tampilan.

Dan tentu saja dapat digunakan dengan banyak cara - misalnya, kita dapat menambahkan avatar pengguna yang masuk ke dalam model.

Metode terakhir yang perlu kita terapkan dalam implementasi HandlerInterceptor kustom adalah afterCompletion ():

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

Saat tampilan berhasil dibuat, kita dapat menggunakan hook ini untuk melakukan hal-hal seperti mengumpulkan statistik tambahan terkait permintaan.

Catatan terakhir yang perlu diingat adalah bahwa HandlerInterceptor terdaftar ke bean DefaultAnnotationHandlerMapping , yang bertanggung jawab untuk menerapkan interseptor ke kelas apa pun yang ditandai dengan anotasi @Controller . Selain itu, Anda dapat menentukan sejumlah interseptor dalam aplikasi web Anda.

5. Interceptor Logger Khusus

Dalam contoh ini kita akan fokus untuk masuk ke aplikasi web kita. Pertama-tama, kelas kita perlu memperluas HandlerInterceptorAdapter :

public class LoggerInterceptor extends HandlerInterceptorAdapter { ... }

Kami juga perlu mengaktifkan logging di pencegat kami:

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

Hal ini memungkinkan Log4J untuk menampilkan log, serta menunjukkan, kelas mana yang saat ini mencatat informasi ke keluaran yang ditentukan.

Selanjutnya, mari kita fokus pada penerapan pencegat khusus:

5.1. Metode preHandle ()

Metode ini dipanggil sebelum menangani permintaan; itu mengembalikan true, untuk memungkinkan framework mengirim permintaan lebih jauh ke metode handler (atau ke interseptor berikutnya). Jika metode mengembalikan false , Spring mengasumsikan bahwa permintaan telah ditangani dan tidak diperlukan pemrosesan lebih lanjut.

Kita dapat menggunakan hook untuk mencatat informasi tentang parameter permintaan: dari mana permintaan itu berasal, dll.

Dalam contoh kami, kami mencatat info ini menggunakan logger Log4J sederhana:

@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 bisa kami lihat, kami mencatat beberapa informasi dasar tentang permintaan tersebut.

Jika kami menemukan kata sandi di sini, kami harus memastikan bahwa kami tidak mencatatnya.

Opsi sederhana adalah mengganti kata sandi, dan jenis data sensitif lainnya, dengan bintang.

Berikut implementasi cepat tentang cara melakukannya:

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.