1. ตัวกรอง servlet
1.1 ตัวกรองคืออะไร
ตัวกรองเป็นโปรแกรมที่ทำงานบนเซิร์ฟเวอร์ก่อนหน้า servlet หรือ jsp ที่เกี่ยวข้อง ตัวกรองอาจแนบกับหนึ่งหน้าหรือมากกว่าหนึ่งหน้าหรือหลายหน้า JSP และอาจตรวจสอบข้อมูลคำขอที่ป้อนทรัพยากรเหล่านี้ หลังจากนี้ตัวกรองสามารถเลือกได้ดังนี้:
①การโทรทรัพยากรอย่างเป็นประจำ (เช่นโทร Servlets หรือหน้า JSP)
②ใช้ข้อมูลคำขอที่แก้ไขเพื่อเรียกทรัพยากร
③เรียกทรัพยากร แต่แก้ไขก่อนที่จะส่งการตอบกลับไปยังลูกค้า
④บล็อกการเรียกทรัพยากรและไปที่ทรัพยากรอื่นส่งคืนรหัสสถานะเฉพาะหรือสร้างเอาต์พุตทดแทน
1.2 หลักการพื้นฐานของตัวกรอง servlet
เมื่อใช้ servlet เป็นตัวกรองจะสามารถประมวลผลคำขอของลูกค้าได้ หลังจากการประมวลผลเสร็จสิ้นจะถูกส่งไปยังตัวกรองถัดไปสำหรับการประมวลผลเพื่อให้คำขอของลูกค้าถูกประมวลผลทีละหนึ่งในห่วงโซ่ตัวกรองจนกว่าคำขอจะถูกส่งไปยังเป้าหมาย ตัวอย่างเช่นเว็บไซต์มีหน้าเว็บที่ส่ง "ข้อมูลการลงทะเบียนที่แก้ไข" หลังจากผู้ใช้กรอกข้อมูลที่แก้ไขแล้วและส่งเซิร์ฟเวอร์ต้องทำงานสองอย่างเมื่อประมวลผล: พิจารณาว่าเซสชันของลูกค้านั้นถูกต้องหรือไม่ และเข้ารหัสข้อมูลที่ส่งอย่างสม่ำเสมอ งานทั้งสองนี้สามารถประมวลผลได้ในห่วงโซ่ตัวกรองซึ่งประกอบด้วยตัวกรองสองตัว เมื่อกระบวนการกรองสำเร็จข้อมูลที่ส่งจะถูกส่งไปยังเป้าหมายสุดท้าย หากกระบวนการกรองไม่สำเร็จมุมมองจะถูกแจกจ่ายไปยังหน้าข้อผิดพลาดที่ระบุ
2. ขั้นตอนการพัฒนาตัวกรอง Servlet
ขั้นตอนสำหรับการพัฒนาตัวกรอง servlet มีดังนี้:
①เขียนคลาส servlet ที่ใช้อินเตอร์เฟสตัวกรอง
②กำหนดค่าตัวกรองใน web.xml
การพัฒนาตัวกรองต้องใช้อินเทอร์เฟซตัวกรอง อินเตอร์เฟสตัวกรองกำหนดวิธีการต่อไปนี้:
① destory () ถูกเรียกโดยเว็บคอนเทนเนอร์เพื่อเริ่มต้นตัวกรองนี้
② init (FilterConfig FilterConfig) เรียกว่าเว็บคอนเทนเนอร์เพื่อเริ่มต้นตัวกรองนี้
③ Dofilter (คำขอ ServletRequest, การตอบสนอง servletResponse, Chain Chain FilterChain) รหัสการประมวลผลการกรองเฉพาะ
3. ตัวอย่างของกรอบตัวกรอง
SimpleFilter1.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ioexception; นำเข้า Javax.servlet.filter; นำเข้า Javax.servlet.filterchain; นำเข้า Javax.servlet.filterconfig; นำเข้า Javax.servlet.servletexception; คลาสสาธารณะ SimpleFilter1 ใช้ตัวกรอง {@suppresswarnings ("ไม่ได้ใช้") ตัวกรองตัวกรองส่วนตัว FilterConfig; public void init (filterConfig config) พ่น servletexception {this.filterConfig = config; } โมฆะสาธารณะ dofilter (คำขอ servletrequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) {ลอง {system.out.println ("ภายใน SimpleFilter1: การกรองคำขอ ... "); chain.dofilter (คำขอ, การตอบกลับ); // ส่งการประมวลผลไปยังระบบตัวกรองถัดไป. } catch (ioexception ioe) {ioe.printstacktrace (); } catch (servletexception se) {se.printstacktrace (); }} โมฆะสาธารณะทำลาย () {this.filterConfig = null; -
SimpleFilter2.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ioexception; นำเข้า Javax.servlet.filter; นำเข้า Javax.servlet.filterchain; นำเข้า Javax.servlet.filterconfig; นำเข้า Javax.servlet.servletexception; คลาสสาธารณะ SimpleFilter2 ใช้ตัวกรอง {@suppresswarnings ("ไม่ได้ใช้") ตัวกรองตัวกรองส่วนตัว FilterConfig; public void init (filterConfig config) พ่น servletexception {this.filterConfig = config; } โมฆะสาธารณะ dofilter (คำขอ servletrequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) {ลอง {system.out.println ("ภายใน SimpleFilter2: กรองคำขอ ... "); chain.dofilter (คำขอ, การตอบกลับ); // ส่งการประมวลผลไปยัง System.out.out.println ("ภายใน SimpleFilter2: กรองการตอบกลับ ... "); } catch (ioexception ioe) {ioe.printstacktrace (); } catch (servletexception se) {se.printstacktrace (); }} โมฆะสาธารณะทำลาย () {this.filterConfig = null; -
web.xml
<Tilter> <filter-Name> Filter1 </filter-name> <s Filter-Class> com.zj.sample.simpleFilter1 </filter-class> </filter> <filter-mapping> <filter-name> filter1 </filter-MApping> <Tilter-Name> Filter2 </tilter-Name> <silter-Class> com.zj.sample.simpleFilter2 </filter-class> </filter> <filter-mapping> <filter-name> filter2 </filter-name> <url-pattern>/*
เปิดหน้าใด ๆ ในเว็บคอนเทนเนอร์เพื่อส่งออกผลลัพธ์: (หมายเหตุคำสั่งคำขอ/การตอบสนองที่ดำเนินการโดยตัวกรอง)
ภายใน SimpleFilter1: การกรองคำขอ ... ภายใน SimpleFilter2: การกรองคำขอ ... ภายใน SimpleFilter2: การกรองการตอบกลับ ... ภายใน SimpleFilter1: การกรองการตอบกลับ ...
4. รายงานตัวกรอง
มาทดลองกับตัวกรองอย่างง่ายที่พิมพ์ข้อความไปยังเอาต์พุตมาตรฐานโดยเรียกหน้า servlet หรือ jsp ที่เกี่ยวข้อง ในการใช้ฟังก์ชั่นนี้พฤติกรรมการกรองจะดำเนินการในวิธี Dofilter เมื่อใดก็ตามที่หน้า servlet หรือ jsp ที่เกี่ยวข้องกับตัวกรองนี้จะถูกเรียกใช้วิธี dofilter จะสร้างงานพิมพ์ที่แสดงรายการโฮสต์ที่ร้องขอและ URL ของการโทร เนื่องจากวิธี getRequesturl อยู่ใน httpservletrequest แทน servletrequest วัตถุ servletrequest จึงถูกสร้างขึ้นเป็นประเภท httpservletrequest ลองเปลี่ยน SimpleFilter1.java ในบทที่ 3
SimpleFilter1.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ioexception; นำเข้า java.util.date นำเข้า javax.servlet.filter; นำเข้า javax.servlet.filterchain; นำเข้า javax.servlet.filterconfig; javax.servlet.servletResponse; นำเข้า Javax.servlet.http.httpservletRequest; คลาสสาธารณะ SimpleFilter1 ใช้ตัวกรอง {@suppresswarnings ("ไม่ได้ใช้") ตัวกรองตัวกรองส่วนตัว FilterConfig; public void init (filterConfig config) พ่น servletexception {this.filterConfig = config; } โมฆะสาธารณะ dofilter (คำขอ servletrequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) {ลอง {system.out.println ("ภายใน SimpleFilter1: การกรองคำขอ ... "); httpservletrequest req = (httpservletrequest) คำขอ; System.out.println (req.getRemoteHost () + "พยายามเข้าถึง" + req.getRequesturl () + "บน" + วันที่ใหม่ () + "."); chain.dofilter (คำขอ, การตอบกลับ); System.out.println ("ภายใน SimpleFilter1: การกรองการตอบกลับ ... "); } catch (ioexception ioe) {ioe.printstacktrace (); } catch (servletexception se) {se.printstacktrace (); }} โมฆะสาธารณะทำลาย () {this.filterConfig = null; -
การตั้งค่า web.xml ยังคงไม่เปลี่ยนแปลงในบทที่ 3 เดียวกัน
ทดสอบ:
ป้อน [url] http: // localhost: 8080/test4jsp/login.jsp [/url]
ผลลัพธ์:
ภายใน SimpleFilter1: การกรองคำขอ ... 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 0: 1 พยายามเข้าถึง [url] http: // localhost: 8080/test4jsp/login.jsp [/url] บนดวงอาทิตย์ 04 17:01:37 SimpleFilter2: กรองการตอบกลับ ... ภายใน SimpleFilter1: การกรองการตอบกลับ ...
5. ตัวกรองที่ Access (ใช้ servlets เพื่อเริ่มต้นพารามิเตอร์ในตัวกรอง)
ต่อไปนี้คือการตั้งค่าช่วงเวลาการเข้าถึงปกติโดยใช้ init เพื่อบันทึกการเข้าถึงที่ไม่ได้อยู่ในช่วงเวลานี้ ลองเปลี่ยน SimpleFilter2.java ในบทที่ 3
SimpleFilter2.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ioexception; นำเข้า java.text.dateformat; นำเข้า java.util.calendar; นำเข้า java.util.gregoriancalendar นำเข้า Javax.servlet.filter; นำเข้า Javax.servlet.filterchain; javax.servlet.servletContext; นำเข้า javax.servlet.servletexception; นำเข้า Javax.servlet.servletrequest; นำเข้า Javax.servlet.servletResponse; นำเข้า Javax.servlet.http.httpservletRequest; คลาสสาธารณะ SimpleFilter2 ใช้ตัวกรอง {@suppresswarnings ("ไม่ได้ใช้") การกำหนดค่าตัวกรองส่วนตัว บริบท ServletContext ส่วนตัว เริ่มต้นเวลาเริ่มต้น รูปแบบวันที่ส่วนตัว; public void init (filterConfig config) พ่น servletexception {this.config = config; บริบท = config.getServletContext (); formatter = dateformat.getDateTimeInstance (dateformat.medium, dateformat.medium); ลอง {starttime = integer.parseint (config.getInitParameter ("starttime")); // web.xml endtime = integer.parseint (config.getInitParameter ("endtime"); // web.xml} catch (numberFormatexception nfe) เริ่มต้น = 22; // 22:00 PM Endtime = 6; // 6:00 AM}} โมฆะสาธารณะ dofilter (คำขอ servletrequest, การตอบสนอง servletResponse, โซ่ฟิลเตอร์เชน) {ลอง {system.out.println ("ภายใน SimpleFilter2: กรองคำขอ ... "); httpservletrequest req = (httpservletrequest) คำขอ; ปฏิทิน Gregoriancalendar = New Gregoriancalendar (); int currenttime = calendar.get (calendar.hour_of_day); if (isunusualtime (currenttime, starttime, endtime)) {context.log ("คำเตือน:" + req.getRemoteHost () + "เข้าถึง" + req.getRequesturl () + "บน" + formatter.format (calendar.getTime ()); // ไฟล์บันทึกอยู่ภายใต้ <catalina_home> /logs. หนึ่งบันทึกต่อวัน } chain.dofilter (คำขอ, การตอบกลับ); System.out .println ("ภายใน SimpleFilter2: การกรองการตอบกลับ ... "); } catch (ioexception ioe) {ioe.printstacktrace (); } catch (servletexception se) {se.printstacktrace (); }} โมฆะสาธารณะทำลาย () {} // เป็นเวลาปัจจุบันระหว่างการเริ่มต้นและสิ้นสุด // เวลาที่ถูกทำเครื่องหมายว่าเป็นเวลาการเข้าถึงที่ผิดปกติหรือไม่? บูลีนส่วนตัว isunusualtime (int currenttime, int starttime, int endtime) {// ถ้าเวลาเริ่มต้นน้อยกว่าเวลาสิ้นสุด (เช่น // พวกเขาสองครั้งในวันเดียวกัน) ดังนั้นเวลา // ปัจจุบันจะถือว่าผิดปกติหากเป็น // ระหว่างเวลาเริ่มต้นและสิ้นสุด if (starttime <endtime) {return ((currenttime> = starttime) && (currenttime <endtime)); } // หากเวลาเริ่มต้นมากกว่าหรือเท่ากับเวลา // สิ้นสุด (เช่นเวลาเริ่มต้นคือในหนึ่งวันและ // เวลาสิ้นสุดในวันถัดไป) ดังนั้นเวลา // เวลาปัจจุบันจะถือว่าผิดปกติหากไม่ได้อยู่ระหว่าง // เวลาสิ้นสุดและเวลาเริ่มต้น else {return (! isunusualtime (currenttime, endtime, starttime)); -
การตั้งค่า web.xml ยังคงไม่เปลี่ยนแปลง
เกี่ยวกับการประมวลผลบันทึก TOMCAT นี่คือการแนะนำเพิ่มเติม config.getServletContext (). log ("ข้อความบันทึก") จะเขียนข้อมูลบันทึกไปยังโฟลเดอร์ <catalina_home>/บันทึก ชื่อไฟล์ควรเป็น localhost_log.2007-03-04.txt (หนึ่งถูกสร้างขึ้นต่อวันและสามารถเห็นได้ในวันถัดไป) ในการรับไฟล์บันทึกดังกล่าวคุณควรมี:
<logger classname = "org.apache.catalina.logger.filelogger" คำนำหน้า = "catalina_log" suffix = ". txt" timestamp = "true"/>
6. ห้ามใช้ตัวกรองไซต์
หากคุณต้องการขัดจังหวะกระบวนการกรองที่ตามมาอยู่กึ่งกลางเมื่อตัวกรองของคุณตรวจพบข้อยกเว้นที่ผิดปกติคุณสามารถทำได้:
โมฆะสาธารณะ dofilter (คำขอ ServletRequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) พ่น ServleTexception, iOException {httpservletRequest req = (httpservletrequest) คำขอ; httpservletResponse res = (httpservletResponse) การตอบสนอง; if (isunusualCondition (req)) {res.sendredirect ("http://www.somesite.com"); } else {chain.dofilter (req, res); - ตัวอย่างต่อไปนี้เป็นตัวกรองไซต์ที่ต้องห้าม หากคุณไม่ต้องการให้เว็บไซต์บางแห่งเข้าถึงเว็บไซต์ของคุณคุณสามารถแสดงรายการเว็บไซต์ในค่าพารามิเตอร์ของ web.xml จากนั้นใช้หลักการด้านบนเพื่อกระโดดออกจากการกรองปกติและให้หน้าต้องห้าม
BannedaccessFilter.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ioexception; นำเข้า java.io.printwriter; นำเข้า java.net.malformedurlexception; นำเข้า java.net.url; นำเข้า java.util.hashset; นำเข้า Java.util.StringTokenizer; javax.servlet.filterconfig; นำเข้า Javax.servlet.servletexception; นำเข้า Javax.servlet.servletrequest; นำเข้า Javax.servlet.servletResponse; นำเข้า Javax.servlet.http.httpservletRequest; คลาสสาธารณะ BannedAccessFilter ใช้ตัวกรอง {HashSet ส่วนตัว <String> BannedSitetable; /*** ปฏิเสธการเข้าถึงหากคำขอมาจากเว็บไซต์ที่ได้รับการแบนเนอร์หรือถูกอ้างถึงที่นี่* โดยเว็บไซต์แบนเนอร์ */ โมฆะสาธารณะ Dofilter (คำขอ ServletRequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) โยน servletexception, ioexception {system.out.println ("ภายใน BannedAccessFilter: การกรองคำขอ ... "); httpservletrequest req = (httpservletrequest) คำขอ; String requestingHost = req.getRemoteHost (); String refreringHost = getReferringHost (req.getheader ("ผู้อ้างอิง")); สตริงแบนไซต์ = null; บูลีน isbanned = false; if (bannedSitetable.contains (ขอ host)) {bannedSite = requestinghost; isbanned = true; } อื่นถ้า (BannedSitetable.contains (refringhost)) {bannedSite = referringHost; isbanned = true; } if (isebanned) {showwarning (การตอบสนอง, bannedsite); } else {chain.dofilter (คำขอ, การตอบกลับ); } system.out.println ("ภายใน BannedAccessFilter: การกรองการตอบกลับ ... "); } /*** สร้างตารางของไซต์แบนเนอร์ตามพารามิเตอร์การเริ่มต้น* โปรดจำไว้ว่าเวอร์ชัน 2.3 ของ Servlet API จะสั่งการใช้แพลตฟอร์ม* Java 2 ดังนั้นจึงปลอดภัยที่จะใช้ hashset (ซึ่งกำหนด* ว่ามีคีย์ที่กำหนดอยู่) มากกว่า hashtable* (ซึ่งมีค่าสำหรับแต่ละคีย์หรือไม่)*/ โมฆะสาธารณะเริ่มต้น (FilterConfig config) โยน servletexception String BannedSites = config.getInitParameter ("BannedSites"); // ชุดโทเค็นเริ่มต้น: พื้นที่สีขาว StringTokenizer tok = new StringTokenizer (BannedSites); ในขณะที่ (tok.hasmoretokens ()) {String bannedsite = tok.nexttoken (); BannedSitetable.add (BannedSite); System.out.println ("ห้าม" + Bannedsite); }} โมฆะสาธารณะทำลาย () {} สตริงส่วนตัว getReferringringHost (String referringurlstring) {ลอง {url referringurl = url ใหม่ (referringurlstring); return (refringurl.getHost ()); } catch (malformedurlexception mue) {// malformed หรือ null return (null); }} // การตอบสนองการแทนที่ที่ส่งคืนไปยังผู้ใช้ // ที่มาจากหรืออ้างอิงที่นี่โดยเว็บไซต์แบนเนอร์ Void Private Void Showwarning (การตอบสนอง servletResponse, String bannedsite) พ่น ServleTexception, iOexception {response.setContentType ("ข้อความ/html"); PrintWriter out = response.getWriter (); String doctype = "<! doctype html public/" // w3c // dtd html 4.0 " +" transitional // en/">/n"; out.println (doctype + "<html>/n" + "<head> <title> การเข้าถึงต้องห้าม </title> </head>/n" + "<body bgcolor =/" White/">/n" + "<h1> การเข้าถึง </h1>/n" + " "</body> </html>"); -
web.xml
<Tilter> <Tilter-Name> BannedAccessFilter </filter-name> <silter-class> com.zj.sample.bannedaccessFilter </filter-class> <init-param> <param-name> BannedSites [url] www.bettersite.com [/url] [url] www.moreservlets.com [/url] 127.0.0.1//we ทดสอบสิ่งนี้ </param-value> <url-pattern>/*</url-pattern> </filter-mapping>
ทดสอบ:
[url] http: // localhost: 8080/test4jsp/[/url]
ผลลัพธ์:
7. แทนที่ตัวกรอง
7.1 แก้ไขการตอบสนอง
ตัวกรองสามารถบล็อกการเข้าถึงทรัพยากรหรือป้องกันไม่ให้เปิดใช้งาน แต่ถ้าตัวกรองต้องการเปลี่ยนการตอบสนองที่สร้างขึ้นโดยทรัพยากร จะทำอย่างไร? ดูเหมือนว่าจะไม่มีวิธีในการเข้าถึงการตอบสนองที่สร้างขึ้นโดยทรัพยากร พารามิเตอร์ที่สองของ DoFilter (ServletResponse) ให้วิธีส่งเอาต์พุตใหม่ไปยังไคลเอนต์ แต่ไม่ได้ให้วิธีการเข้าถึงตัวกรองในการเข้าถึงเอาต์พุตหน้า servlet หรือ jsp ทำไมสิ่งนี้ถึงเกิดขึ้น? เนื่องจากหน้า servlet หรือ jsp ไม่ได้ถูกดำเนินการเมื่อมีการเรียกวิธี Dofilter เป็นครั้งแรก เมื่อมีการเรียกวิธี Dofilter ในวัตถุ FilterChain ดูเหมือนว่าจะสายเกินไปที่จะแก้ไขการตอบสนองซึ่งก็คือว่าข้อมูลถูกส่งไปยังลูกค้า
อย่างไรก็ตามมีวิธีการแก้ไขวัตถุการตอบสนองของวิธี Dofilter ที่ส่งผ่านไปยังวัตถุ FilterChain โดยทั่วไปสร้างแคชของเวอร์ชันเอาต์พุตทั้งหมดที่สร้างขึ้นโดยหน้า servlet หรือ jsp Servlet API เวอร์ชัน 2.3 ให้ทรัพยากรที่มีประโยชน์สำหรับสิ่งนี้คือคลาส HTTPSERVLETRESPONSEWRAPPER การใช้คลาสนี้มีห้าขั้นตอนต่อไปนี้:
1) สร้าง wrapper ตอบกลับ ขยาย Javax.servlet.http.httpservletResponSewrapper
2) ให้นักพิมพ์ที่แคชเอาท์พุท โอเวอร์โหลดเมธอด getWriter ส่งคืน printwriter ที่บันทึกทุกอย่างที่ส่งไปยังและบันทึกผลลัพธ์ลงในฟิลด์ที่สามารถเข้าถึงได้ในภายหลัง
3) ผ่านเสื้อคลุมนี้ไปยัง Dofilter การโทรนี้ถูกกฎหมายเนื่องจาก httpservletResponSewrapper ใช้ httpservletResponse
4) แยกและแก้ไขเอาต์พุต หลังจากเรียกใช้วิธี Dofilter ของ FilterChain แล้วเอาต์พุตของทรัพยากรดั้งเดิมสามารถรับได้โดยใช้กลไกที่ให้ไว้ในขั้นตอนที่ 2 คุณสามารถแก้ไขหรือแทนที่ได้ตราบใดที่เหมาะสำหรับแอปพลิเคชันของคุณ
5) ส่งเอาต์พุตที่แก้ไขไปยังไคลเอนต์ เนื่องจากทรัพยากรดั้งเดิมไม่ส่งเอาต์พุตไปยังไคลเอนต์อีกต่อไป (เอาต์พุตเหล่านี้ถูกเก็บไว้ใน wrapper ตอบกลับของคุณแล้ว) จะต้องส่งเอาต์พุตเหล่านี้ ด้วยวิธีนี้ตัวกรองของคุณจะต้องได้รับ printwriter หรือ outputstream จากวัตถุตอบกลับดั้งเดิมและส่งเอาต์พุตที่แก้ไขลงในสตรีม
7.2 wrapper ตอบกลับที่ใช้ซ้ำได้
ตัวอย่างต่อไปนี้ให้เสื้อคลุมที่สามารถใช้ในแอปพลิเคชันส่วนใหญ่ที่ตัวกรองต้องการแก้ไขเอาต์พุตของทรัพยากร คลาส ChararrayWrapper โอเวอร์โหลดเมธอด GetWriter เพื่อส่งคืน Printwriter ซึ่งสะสมทุกอย่างในอาร์เรย์ตัวละครขนาดใหญ่ นักพัฒนาสามารถรับผลลัพธ์นี้ได้โดยใช้ tochararray (ต้นฉบับถ่าน []) หรือ toString (สตริงที่ได้จากถ่าน [])
chararraywrapper.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.chararraywriter; นำเข้า java.io.printwriter; นำเข้า Javax.servlet.http.httpservletResponse; นำเข้า javax.servlet.http.httpservleswleswrapper; /** * wrapper ตอบกลับที่ใช้ทุกอย่างตามปกติลูกค้าจะ * ส่งออกและบันทึกไว้ในอาร์เรย์ตัวละครขนาดใหญ่ตัวหนึ่ง */ชั้นเรียนสาธารณะ ChararrayWrapper ขยาย HttpservletResponSewrapper {Private ChararrayWriter Charwriter; /*** เริ่มต้น wrapper * <p> * อันดับแรกตัวสร้างนี้เรียกตัวสร้างหลัก การโทร *นั้นโหดร้ายเพื่อให้การตอบสนองถูกเก็บไว้และทำให้ setheader, *setstatus, addcookie และอื่น ๆ ทำงานตามปกติ * <p> * ที่สองตัวสร้างนี้สร้าง chararraywriter ที่จะใช้ * เพื่อสะสมการตอบสนอง */ Public ChararrayWrapper (การตอบสนอง httpservletResponse) {super (การตอบสนอง); charwriter = chararrayWriter ใหม่ (); } /*** เมื่อหน้า servlets หรือ jsp ขอให้นักเขียนอย่าให้พวกเขา* ของจริง ให้เวอร์ชันที่เขียนลงใน* อาร์เรย์อักขระแทน * ตัวกรองจำเป็นต้องส่งเนื้อหาของอาร์เรย์ไปยังไคลเอนต์* (อาจหลังจากแก้ไข) */ Public PrintWriter GetWriter () {return (ใหม่ PrintWriter (Charwriter)); } /*** รับการแสดงสตริงของบัฟเฟอร์ทั้งหมด * <p> * ต้องแน่ใจว่า <b> ไม่ </b> เรียกใช้วิธีนี้หลายครั้งบน wrapper * เดียวกัน API สำหรับ ChararrayWriter ไม่รับประกันว่า * "จำ" ค่าก่อนหน้านี้ดังนั้นการโทรมีแนวโน้มที่จะทำให้ * สตริงใหม่ทุกครั้ง */ สตริงสาธารณะ toString () {return (charwriter.toString ()); } /** รับอาร์เรย์อักขระพื้นฐาน */ ถ่านสาธารณะ [] tochararray () {return (charwriter.tochararray ()); -
7.3 แทนที่ตัวกรอง
นี่คือแอปพลิเคชั่นทั่วไปของ ChararrayWrapper ที่ให้ไว้ในส่วนก่อนหน้า: การเปลี่ยนตัวกรองสำหรับสตริงเป้าหมายที่เกิดขึ้นหลายครั้งเป็นสตริงทดแทน
7.3.1 ตัวกรองทดแทนทั่วไป
แทนที่ filter.java ให้ตัวกรองที่ห่อการตอบสนองใน ChararraryWrapper ผ่าน wrapper ไปยังวิธี dofilter ของวัตถุ filterchain แยกค่าประเภทสตริงให้เอาต์พุตของทรัพยากรทั้งหมดแทนที่ทั้งหมดของสตริงเป้าหมายด้วยสตริงการทดแทนและส่งผลลัพธ์ที่ปรับเปลี่ยนไปยังลูกค้า
มีสองสิ่งที่ควรทราบเกี่ยวกับตัวกรองนี้ ครั้งแรกมันเป็นคลาสนามธรรม ในการใช้งานคุณต้องสร้างคลาสย่อยที่ให้การใช้งานของ GetTargetString และวิธีการ getReplacementString ตัวอย่างของการรักษานี้ได้รับในส่วนย่อยถัดไป ประการที่สองมันใช้คลาสยูทิลิตี้ขนาดเล็ก (ดู filterutils.java) สำหรับการเปลี่ยนสตริงจริง คุณสามารถใช้แพ็คเกจนิพจน์ทั่วไปใหม่แทนที่จะใช้วิธีการระดับต่ำและน่าเบื่อในสตริงและสตริง
แทนที่ filter.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ioexception; นำเข้า java.io.printwriter; นำเข้า Javax.servlet.filter; นำเข้า javax.servlet.filterchain; นำเข้า javax.servlet.filterconfig; javax.servlet.servletResponse; นำเข้า Javax.servlet.http.httpservletResponse; /*** ตัวกรองที่แทนที่การเกิดขึ้นทั้งหมดของสตริงที่กำหนดด้วยการแทนที่ A* * นี่คือคลาสที่เป็นนามธรรม: คุณ <i> ต้อง </i> แทนที่วิธีการ getTargetString* และ getReplacementString ใน subclass* วิธีแรกของวิธีการเหล่านี้ระบุสตริงในการตอบสนอง* ที่ควรเปลี่ยน ที่สองของข้อกำหนดเหล่านี้สตริง* ที่ควรแทนที่แต่ละครั้งของสตริงเป้าหมาย */Public Abstract Class Public Filter ใช้ตัวกรอง {private filterConfig config; โมฆะสาธารณะ Dofilter (คำขอ ServletRequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) โยน servletexception, ioException {ChararrayWrapper Responsewrapper = ใหม่ ChararrayWrapper ((httpservletResponse) การตอบสนอง); // เรียกใช้ทรัพยากรสะสมเอาต์พุตใน wrapper chain.dofilter (คำขอ, Responsewrapper); // เปลี่ยนเอาต์พุตทั้งหมดเป็นหนึ่งสตริงใหญ่ String Responsestring = ResponseWrapper.toString (); // ในเอาต์พุตให้แทนที่การเกิดขึ้นทั้งหมดของสตริงเป้าหมายด้วยการแทนที่ // สตริง Responsestring = filterutils.replace (responsestring, getTargetString (), getReplacementString ()); // อัปเดตส่วนหัวความยาวเนื้อหา UpdateHeaders (การตอบสนองการตอบสนอง); PrintWriter out = response.getWriter (); out.write (Responsestring); } /*** จัดเก็บวัตถุ FilterConfig ในกรณีที่ subclasses ต้องการ */ โมฆะสาธารณะ init (filterConfig config) พ่น servletexception {this.config = config; } protected filterConfig getFilterConfig () {return (config); } โมฆะสาธารณะทำลาย () {} /*** สตริงที่ต้องการการเปลี่ยน* แทนที่วิธีนี้ในคลาสย่อยของคุณ */ บทคัดย่อสตริงสาธารณะ getTargetString (); /*** สตริงที่แทนที่เป้าหมาย แทนที่วิธีนี้ใน * คลาสย่อยของคุณ */ สตริงนามธรรมสาธารณะ getReplacementString (); /*** อัปเดตส่วนหัวการตอบกลับ เวอร์ชันง่าย ๆ นี้เพียงแค่ตั้งส่วนหัวความยาวเนื้อหาโดยสมมติว่าเราใช้ชุดอักขระ* ที่ใช้ 1 ไบต์ต่ออักขระ* สำหรับชุดอักขระอื่นแทนที่วิธีนี้เพื่อใช้* ตรรกะที่แตกต่างกันหรือยอมแพ้ในการเชื่อมต่อ HTTP แบบถาวร* ในกรณีหลังนี้ */ โมฆะสาธารณะ updateHeaders (การตอบสนอง servletResponse, การตอบสนองของสตริง) {response.setContentLength (Responsestring.length ()); -
filterutils.java
แพ็คเกจ com.zj.sample; /*** ยูทิลิตี้ขนาดเล็กเพื่อช่วยในการห่อหุ้มที่กลับมาสตริง * /คลาสสาธารณะ FilterUtils { /*** เปลี่ยนเหตุการณ์ทั้งหมดของต้นกำเนิดในกระแสหลักเพื่อแทนที่ */ สตริงคงที่สาธารณะแทนที่ (String Mainstring, String Orig, String Replacement) {String result = ""; int oldIndex = 0; ดัชนี int = 0; int origlength = orig.length (); ในขณะที่ ((index = mainstring.indexof (orig, oldindex))! = -1) {result = ผลลัพธ์ + mainstring.substring (oldindex, index) + การแทนที่; oldIndex = index + origlength; } result = ผลลัพธ์ + mainstring.substring (oldIndex); return (ผลลัพธ์); }} 7.3.2 ใช้ตัวกรองการเปลี่ยนอักขระ สมมติว่า Baidu ได้รับ Google (แค่สมมติฐาน) ข้อความทั้งหมดที่มีคำว่า Google ในทุกหน้าจะต้องถูกแทนที่ด้วย Baidu! OlplacesitenameFilter.java สืบทอด ouplicfilter.java ด้านบนเพื่อใช้ฟังก์ชั่นนี้ แทนที่เซียร์เมียร์ฟิลเตอร์. javapackage com.zj.sample; คลาสสาธารณะแทนที่จะใช้งาน Filter ขยาย OplightFilter {สตริงสาธารณะ getTargetString () {return ("google.com.cn"); } สตริงสาธารณะ getReplacementString () {return ("baidu.com"); -
web.xml
<silter> <filter-name> แทนที่ replacesitenamefilter </filter-name> <filter-class> com.zj.sample.replacesitenamefilter </filter-class> </filter>
ผลการทดสอบ:
ก่อนกรอง
หลังจากกรอง
8. ตัวกรองการบีบอัด
มีเบราว์เซอร์ล่าสุดหลายแห่งที่สามารถจัดการเนื้อหาที่ถูกบีบอัดได้โดยไม่ได้แกะไฟล์ที่ถูกบีบอัดโดยอัตโนมัติด้วย GZIP เป็นค่าส่วนหัวการตอบกลับการเข้ารหัสเนื้อหาแล้วประมวลผลผลลัพธ์เช่นเดียวกับเอกสารต้นฉบับ การส่งเนื้อหาที่บีบอัดดังกล่าวสามารถประหยัดเวลาได้มากเนื่องจากเวลาที่ใช้ในการบีบอัดเอกสารบนเซิร์ฟเวอร์แล้วเลิกทำเอกสารบนไคลเอนต์นั้นเล็กน้อยเมื่อเทียบกับเวลาที่ใช้ในการดาวน์โหลดไฟล์ โปรแกรม longservlet.java ให้ servlet ที่มีเอาต์พุตข้อความธรรมดาที่มีความยาวและซ้ำกัน servlet สำหรับการบีบอัด หากคุณใช้ GZIP มันสามารถบีบอัดผลลัพธ์ที่ 1/300!
เมื่อเบราว์เซอร์รองรับความสามารถในการบีบอัดนี้ตัวกรองการบีบอัดสามารถใช้ ChararrayWrapper ที่แนะนำในบทที่ 7 เพื่อบีบอัดเนื้อหา ต้องใช้เนื้อหาต่อไปนี้เพื่อให้งานนี้เสร็จสมบูรณ์:
1) คลาสที่ใช้อินเทอร์เฟซตัวกรอง ชั้นเรียนนี้มีชื่อว่า PrompressionFilter วิธีการเริ่มต้นเก็บวัตถุ FilterConfig ในฟิลด์ในกรณีที่ subclass จำเป็นต้องเข้าถึงสภาพแวดล้อม servlet หรือชื่อตัวกรอง ร่างกายของวิธีการชะตากรรมนั้นว่างเปล่า
2) วัตถุตอบสนองที่ห่อหุ้ม วิธีการ Dofilter ห่อวัตถุ servletresponse ใน chararraywrapper และส่ง wrapper นี้ไปยังวิธี dofilter ของวัตถุ FilterChain หลังจากการโทรนี้เสร็จสมบูรณ์แล้วตัวกรองอื่น ๆ และทรัพยากรขั้นสุดท้ายทั้งหมดได้ถูกดำเนินการและเอาต์พุตอยู่ในเสื้อคลุม ด้วยวิธีนี้ Dofilter ดั้งเดิมจะดึงอาร์เรย์ของตัวละครที่แสดงถึงผลลัพธ์ของทรัพยากรทั้งหมด หากลูกค้าระบุว่ารองรับการบีบอัด (เช่นการใช้ GZIP เป็นค่าสำหรับส่วนหัวที่ยอมรับการยอมรับ) ตัวกรองจะผนวก GZIPOUTPUTSTREAM ไปยัง ByTeArrayOutputStream คัดลอกอาร์เรย์อักขระลงในสตรีมนี้และตั้งค่าส่วนหัวการตอบสนองการเข้ารหัสเนื้อหา หากไคลเอนต์ไม่รองรับ GZIP ให้คัดลอกอาร์เรย์อักขระที่ไม่ได้แก้ไขไปยัง ByTeArrayOutputStream ในที่สุด Dofilter จะส่งผลลัพธ์ไปยังลูกค้าโดยการเขียนอาร์เรย์อักขระทั้งหมด (อาจบีบอัด) ลงใน outputstream ที่เกี่ยวข้องกับการตอบกลับดั้งเดิม
3) ลงทะเบียน Longservlet
การบีบอัด filter.java
แพ็คเกจ com.zj.sample; นำเข้า java.io.ByTeArrayOutputStream; นำเข้า java.io.ioException; นำเข้า java.io.OutputStream; นำเข้า Java.io.OutputStreamWriter; นำเข้า Java.util.zip.gzipOutputstream; javax.servlet.filterconfig; นำเข้า javax.servlet.servletexception; นำเข้า Javax.servlet.servletrequest; นำเข้า Javax.servlet.servletResponse; นำเข้า Javax.servlet.http.httpservletRequest; /** * ตัวกรองที่บีบอัดเอาต์พุตด้วย GZIP (สมมติว่าเบราว์เซอร์รองรับ * GZIP) */Public Class CompressionFilter ใช้ตัวกรอง {private filterConfig config; /*** หากเบราว์เซอร์ไม่รองรับ GZIP ให้เรียกใช้ทรัพยากรตามปกติ หากเบราว์เซอร์ * <i> ไม่สนับสนุน GZIP ให้ตั้งค่าส่วนหัวการตอบสนองการเข้ารหัสเนื้อหาและ * เรียกใช้ทรัพยากรด้วยการตอบกลับที่ห่อหุ้มซึ่งรวบรวมเอาต์พุตทั้งหมด * แยกเอาต์พุตและเขียนลงในอาร์เรย์ไบต์ GZIPPED ในที่สุดเขียน * อาร์เรย์นั้นไปยังสตรีมเอาต์พุตของลูกค้า */ โมฆะสาธารณะ Dofilter (คำขอ ServletRequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) โยน servleTexception, iOexception {httpservletRequest req = (httpservletrequest) คำขอ; httpservletResponse res = (httpservletResponse) การตอบสนอง; if (! isgzipsupported (req)) {// เรียกใช้ทรัพยากรตามปกติ chain.dofilter (req, res); } else {// บอกเบราว์เซอร์เรากำลังส่งข้อมูล gzipped Res.Setheader ("การเข้ารหัสเนื้อหา", "GZIP"); // เรียกใช้ทรัพยากรสะสมเอาต์พุตใน wrapper ChararrayWrapper ResponseWrapper = ใหม่ ChararrayWrapper (res); chain.dofilter (req, Responsewrapper); // รับอาร์เรย์อักขระแทนเอาต์พุต char [] responsechars = responsewrapper.tochararray (); // สร้างนักเขียนที่บีบอัดข้อมูลและใส่ลงในอาร์เรย์ไบต์ ByTeArrayOutputStream bytestream = new ByTeArrayOutputStream (); gzipoutputStream zipout = ใหม่ gzipoutputStream (bytestream); OutputStreamWriter tempout = new OutputStreamWriter (zipout); // บีบอัดเอาต์พุตดั้งเดิมและใส่ลงในอาร์เรย์ไบต์ tempout.write (ResponseChars); // GZIP สตรีมจะต้องปิดอย่างชัดเจน tempout.close (); // อัปเดตส่วนหัวความยาวเนื้อหา res.setContentLength (bytestream.size ()); // ส่งผลลัพธ์บีบอัดไปยังไคลเอนต์ outputStream realout = res.getOutputStream (); bytestream.writeto (Realout); }} /*** จัดเก็บวัตถุ FilterConfig ในกรณีที่ subclasses ต้องการ */ โมฆะสาธารณะ init (filterConfig config) พ่น servletexception {this.config = config; } protected filterConfig getFilterConfig () {return (config); } โมฆะสาธารณะทำลาย () {} บูลีนส่วนตัว isgzipsupported (httpservletrequest req) {สตริง browserencodings = req.getheader ("ยอมรับการเข้ารหัส"); return ((browserencodings! = null) && (browserencodings.indexof ("gzip")! = -1)); }} longservlet.javapackage com.zj.sample; นำเข้า java.io.ioexception; นำเข้า java.io.printwriter; นำเข้า javax.servlet.servletexception; นำเข้า javax.servlet.http.httpservlet; javax.servlet.http.httpservletResponse; /*** servlet ที่มี <b> ยาว </b> เอาต์พุต ใช้เพื่อทดสอบผลกระทบของตัวกรองการบีบอัด * ของบทที่ 9 */ LongServlet ระดับสาธารณะขยาย HTTPSERVLET {โมฆะสาธารณะ DOGET (HTTPSERVLETREQUEST Request, HTTPSERVLETRESSESSESSENTS) โยน ServleTexception, iOexception PrintWriter out = response.getWriter (); String doctype = "<! doctype html public/" // w3c // dtd html 4.0 " +" transitional // en/">/n"; String title = "Long Page"; out.println (doctype + "<html>/n" + "<head> <title>" + title + "</title> </head>/n" + "<body bgcolor =/"#fdf5e6/">/n" + " String line = "blah, blah, blah, blah, blah." + "yadda, yadda, yadda, yadda."; สำหรับ (int i = 0; i <10,000; i ++) {out.println (บรรทัด); } out.println ("</body> </html>"); -
web.xml
<silter> <sfilter-name> การบีบอัด Filter </filter-name> <silter-class> com.zj.sample.compressionFilter </filter-class> </filter> <filter-mapping> <servlet-name> longservlet </servlet-name> <servlet-class> com.zj.sample.longservlet </servlet-class> </servlet> <servlet-mapping>