קריאת HttpServletRequest פעמים מרובות באביב

1. הקדמה

במדריך זה נלמד כיצד לקרוא את הגוף מה- HttpServletRequest מספר רב של פעמים באמצעות אביב.

HttpServletRequest הוא ממשק שחושף getInputStream () שיטה לקריאת הגוף. כברירת מחדל, הנתונים מכאן InputStream ניתן לקרוא רק פעם אחת.

2. תלות Maven

הדבר הראשון שנצטרך הוא המתאים אביב-webmvc ו javax.servlet תלות:

 org.springframework spring-webmvc 5.2.0.RELEASE javax.servlet javax.servlet-api 4.0.1 

כמו כן, מכיוון שאנו משתמשים ב- יישום / json סוג התוכן, ה jackson-databind נדרשת תלות:

 com.fasterxml.jackson.core jackson-databind 2.10.0 

Spring משתמש בספרייה זו כדי להמיר אל JSON וממנו.

3. האביב ContentCachingRequestWrapper

האביב מספק א ContentCachingRequestWrapper מעמד. מחלקה זו מספקת שיטה, getContentAsByteArray () לקרוא את הגוף מספר פעמים.

לשיעור זה יש מגבלה, אם כי: איננו יכולים לקרוא את הגופה מספר פעמים באמצעות ה- getInputStream () ו getReader () שיטות.

מחלקה זו מטמנת את גוף הבקשה באמצעות צריכת ה- InputStream. אם אנו קוראים את InputStream באחד המסננים, ואז פילטרים אחרים שלאחר מכן בשרשרת המסננים אינם יכולים לקרוא אותו יותר. בגלל מגבלה זו, שיעור זה אינו מתאים בכל המצבים.

כדי להתגבר על מגבלה זו, בואו נסתכל על פיתרון כללי יותר.

4. הארכה HttpServletRequest

בואו ניצור כיתה חדשה - CachedBodyHttpServletRequest - שמתארך HttpServletRequestWrapper. בדרך זו, איננו צריכים לעקוף את כל השיטות המופשטות של HttpServletRequest מִמְשָׁק.

HttpServletRequestWrapper בכיתה יש שתי שיטות מופשטות getInputStream () ו getReader (). נעקוף את שתי השיטות הללו וניצור קונסטרוקטור חדש.

4.1. הקונסטרוקטור

ראשית, בואו ניצור קונסטרוקטור. בתוכו נקרא את הגופה ממשית InputStream ולאחסן אותו ב- בתים [] לְהִתְנַגֵד:

מחלקה ציבורית CachedBodyHttpServletRequest מרחיב HttpServletRequestWrapper {בייט פרטי [] cachedBody; ציבור CachedBodyHttpServletRequest (HttpServletRequest בקשה) זורק IOException {super (בקשה); InputStream requestInputStream = request.getInputStream (); this.cachedBody = StreamUtils.copyToByteArray (requestInputStream); }}

כתוצאה מכך נוכל לקרוא את הגופה מספר פעמים.

4.2. getInputStream ()

לאחר מכן, בואו נעקוף את getInputStream () שיטה. נשתמש בשיטה זו כדי לקרוא את הגוף הגולמי ולהמיר אותו לאובייקט.

בשיטה זו, אנו ליצור ולהחזיר אובייקט חדש של CachedBodyServletInputStream מעמד (יישום של ServletInputStream):

@Override ציבור ServletInputStream getInputStream () זורק IOException {להחזיר CachedBodyServletInputStream חדש (this.cachedBody); }

4.3. getReader ()

לאחר מכן, נעקוף את getReader () שיטה. שיטה זו מחזירה א BufferedReader לְהִתְנַגֵד:

@Override ציבור BufferedReader getReader () זורק IOException {ByteArrayInputStream byteArrayInputStream = ByteArrayInputStream חדש (this.cachedBody); להחזיר BufferedReader חדש (InputStreamReader חדש (byteArrayInputStream)); }

5. יישום של ServletInputStream

בואו ניצור מעמד - CachedBodyServletInputStream - אשר ייושם ServletInputStream. בשיעור זה ניצור קונסטרוקטור חדש וכן נעקוף את ה- הסתיים(), מוכן() ו לקרוא() שיטות.

5.1. הקונסטרוקטור

ראשית, בואו ניצור קונסטרוקטור חדש שלוקח מערך בתים.

בתוכו ניצור חָדָשׁ ByteArrayInputStream למשל באמצעות מערך בתים זה. לאחר מכן נקצה אותו למשתנה הגלובלי cachedBodyInputStream:

מחלקה ציבורית CachedBodyServletInputStream מרחיב את ServletInputStream {פרטי InputStream cachedBodyInputStream; CachedBodyServletInputStream ציבורי (בתים [] cachedBody) {this.cachedBodyInputStream = ByteArrayInputStream חדש (cachedBody); }}

5.2. לקרוא()

לאחר מכן, נעקוף את לקרוא() שיטה. בשיטה זו נתקשר ByteArrayInputStream # קרא:

@Override int int public () זורק IOException {להחזיר cachedBodyInputStream.read (); }

5.3. הסתיים()

לאחר מכן, נעקוף את הסתיים() שיטה. שיטה זו מציינת האם InputStream יש יותר נתונים לקרוא או לא. זה חוזר נָכוֹן כאשר אפס בתים זמינים לקריאה:

@Override בוליאני ציבורי isFinished () {return cachedBody.available () == 0; }

5.4. מוכן()

באופן דומה, נעקוף את מוכן() שיטה. שיטה זו מציינת האם InputStream מוכן לקריאה או לא.

מכיוון שכבר העתקנו InputStream במערך בתים נחזור נָכוֹן כדי לציין שהוא תמיד זמין:

@Override בוליאני ציבורי isReady () {להחזיר נכון; }

6. המסנן

לבסוף, בואו ניצור מסנן חדש לשימוש ב- CachedBodyHttpServletRequest מעמד. כאן נאריך את האביב OncePerRequestFilter מעמד. בכיתה זו יש שיטה מופשטת doFilterInternal ().

בשיטה זו, אנו ליצור אובייקט של CachedBodyHttpServletRequest בכיתה מאובייקט הבקשה בפועל:

CachedBodyHttpServletRequest cachedBodyHttpServletRequest = חדש CachedBodyHttpServletRequest (בקשה);

ואז אנחנו העבר את אובייקט עטיפת הבקשה החדש לשרשרת המסננים. אז כל השיחות הבאות אל getInputStream() השיטה תפעיל את השיטה שנדרסה:

filterChain.doFilter (cachedContentHttpServletRequest, תגובה);

7. מסקנה

במדריך זה עברנו במהירות את ContentCachingRequestWrapper מעמד. ראינו גם את המגבלות שלה.

לאחר מכן, יצרנו יישום חדש של ה- HttpServletRequestWrapper מעמד. אנו מגזים ב getInputStream () שיטה להחזרת אובייקט של ServletInputStream מעמד.

לבסוף, יצרנו פילטר חדש שיעביר את אובייקט עטיפת הבקשה לשרשרת המסננים. אז הצלחנו לקרוא את הבקשה מספר פעמים.

קוד המקור המלא של הדוגמאות ניתן למצוא באתר GitHub.


$config[zx-auto] not found$config[zx-overlay] not found