รู้เบื้องต้นเกี่ยวกับ Spring MVC HandlerInterceptor

1. บทนำ

ในบทช่วยสอนนี้เราจะเน้นไปที่การทำความเข้าใจ Spring MVC HandlerInterceptorและวิธีใช้อย่างถูกต้อง

2. สปริง MVC Handler

เพื่อให้เข้าใจถึง interceptor ให้ใช้เวลากลับไปตอนและดูที่HandlerMapping วิธีนี้จะแมปเมธอดกับ URL เพื่อที่DispatcherServletจะสามารถเรียกใช้เมื่อประมวลผลคำขอ

และDispatcherServletใช้HandlerAdapterเพื่อเรียกใช้เมธอด

ตอนนี้เราเข้าใจบริบทโดยรวม - นี่คือที่ interceptor จัดการมาใน เราจะใช้HandlerInterceptorเพื่อดำเนินการก่อนที่จะจัดการหลังจากจัดการหรือหลังจากเสร็จสิ้น (เมื่อมีการแสดงผลมุมมอง) ของคำขอ

เครื่องสกัดกั้นสามารถใช้สำหรับข้อกังวลเกี่ยวกับการตัดข้ามและเพื่อหลีกเลี่ยงรหัสตัวจัดการซ้ำ ๆ เช่นการบันทึกการเปลี่ยนพารามิเตอร์ที่ใช้ทั่วโลกในรุ่น Spring เป็นต้น

ในสองสามส่วนถัดไปนั่นคือสิ่งที่เรากำลังจะดูนั่นคือความแตกต่างระหว่างการใช้งานตัวสกัดกั้นต่างๆ

3. การพึ่งพา Maven

ในการใช้Interceptorsคุณต้องรวมส่วนต่อไปนี้ในส่วนการอ้างอิงของไฟล์pom.xmlของคุณ:

 org.springframework spring-web 5.2.8.RELEASE 

เวอร์ชันล่าสุดได้ที่นี่

4. Spring Handler Interceptor

Interceptors ที่ทำงานกับ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 ว่าคำร้องขอควรได้รับการประมวลผลเพิ่มเติมโดยตัวจัดการ ( จริง ) หรือไม่ ( เท็จ )

ต่อไปเรามีการใช้งาน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. Custom Logger Interceptor

ในตัวอย่างนี้เราจะเน้นไปที่การเข้าสู่ระบบเว็บแอปพลิเคชันของเรา ก่อนอื่นคลาสของเราต้องขยายHandlerInterceptorAdapter :

public class LoggerInterceptor extends HandlerInterceptorAdapter { ... }

นอกจากนี้เรายังต้องเปิดใช้งานการบันทึกในตัวสกัดกั้นของเรา:

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

สิ่งนี้ช่วยให้ Log4J แสดงบันทึกรวมทั้งระบุว่าคลาสใดกำลังบันทึกข้อมูลไปยังเอาต์พุตที่ระบุ

ต่อไปเรามาเน้นที่การใช้งานตัวสกัดกั้นแบบกำหนดเอง:

5.1. วิธีการpreHandle ()

วิธีนี้เรียกก่อนจัดการคำขอ มันส่งคืนจริงเพื่อให้เฟรมเวิร์กส่งคำร้องขอเพิ่มเติมไปยังเมธอดตัวจัดการ (หรือไปยังตัวสกัดกั้นถัดไป) หากวิธีการคืนค่าเป็นเท็จ Spring จะถือว่าคำขอนั้นได้รับการจัดการแล้วและไม่จำเป็นต้องดำเนินการใด ๆ เพิ่มเติม

เราสามารถใช้ hook เพื่อบันทึกข้อมูลเกี่ยวกับพารามิเตอร์ของคำขอ: ที่มาของคำขอ ฯลฯ

ในตัวอย่างของเราเรากำลังบันทึกข้อมูลนี้โดยใช้ 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 ()จะถูกเรียกใช้ในลำดับย้อนกลับ

หากเราใช้ Spring Boot แทน vanilla Spring เราควรจำไว้ว่าอย่าใส่คำอธิบายประกอบคลาสการกำหนดค่าของเราด้วย@EnableWebMvcมิฉะนั้นเราจะสูญเสียการกำหนดค่าอัตโนมัติของ Boot

7. สรุป

บทช่วยสอนนี้เป็นบทแนะนำโดยย่อเกี่ยวกับการสกัดกั้นคำขอ HTTP โดยใช้ Spring MVC Handler Interceptor

ตัวอย่างและการกำหนดค่าทั้งหมดมีอยู่ที่ GitHub