מסנני ג'רזי ומיירטים

1. הקדמה

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

נשתמש ב- Jersey 2 כאן ונבדוק את היישום שלנו באמצעות שרת Tomcat 9.

2. הגדרת יישום

בואו ניצור תחילה משאב פשוט בשרת שלנו:

@ Path ("/ greetings") ברכה בכיתה ציבורית {@ GET מחרוזת ציבורית getHelloGreeting () {החזירו "שלום"; }}

בנוסף, בואו ניצור את תצורת השרת המתאימה ליישום שלנו:

@ApplicationPath ("/ *") מחלקה ציבורית ServerConfig מרחיב את ResourceConfig {public ServerConfig () {חבילות ("com.baeldung.jersey.server"); }}

אם ברצונך להעמיק כיצד ליצור ממשק API עם ג'רזי, תוכל לעיין במאמר זה.

אתה יכול גם להעיף מבט במאמר שלנו הממוקד בלקוח וללמוד כיצד ליצור לקוח ג'אווה עם ג'רזי.

3. פילטרים

עכשיו, בואו נתחיל עם פילטרים.

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

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

3.1. יישום מסנן שרת בקשה

נתחיל עם המסננים בצד השרת וניצור מסנן בקשות.

נעשה זאת על ידי יישום ה- ContainerRequestFilter ממשק ורישום אותו כ- ספק בשרת שלנו:

@Provider class class RestrictedOperationsRequestFilter מיישם ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) זורק IOException {if (ctx.getLanguage ()! = Null && "EN" .equals (ctx.getLanguage (). .abortWith (Response.status (Response.Status.FORBIDDEN) .entity ("אין אפשרות לגשת") .build ()); }}}

המסנן הפשוט הזה פשוט דוחה את הבקשות עם השפה "EN" בבקשה בטלפון abortWith () שיטה.

כפי שהדוגמה מראה, היינו צריכים ליישם רק שיטה אחת שמקבלת את הקשר של הבקשה, אותה נוכל לשנות לפי הצורך.

בואו נזכור את זה מסנן זה מבוצע לאחר התאמת המשאב.

במקרה שנרצה לבצע פילטר לפני התאמת המשאבים, אנו יכולים להשתמש במסנן תואם מראש על ידי הערת המסנן שלנו עם ה- @ PreMatching ביאור:

@Provider @ PreMatching מחלקה ציבורית PrematchingRequestFilter מיישם ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) זורק IOException {if (ctx.getMethod (). שווה ("DELETE")) {LOG.info (")" }}}

אם ננסה לגשת למשאב שלנו עכשיו, נוכל לבדוק שהפילטר שלנו לפני ההתאמה מבוצע תחילה:

2018-02-25 16: 07: 27,800 [http-nio-8080-exec-3] INFO cbjsfPrematchingRequestFilter - מסנן התאמה מוקדמת 2018-02-25 16: 07: 27,816 [http-nio-8080-exec-3] מידע cbjsf RestrictedOperationsRequestFilter - מסנן פעולות מוגבלות

3.2. הטמעת מסנן שרת תגובה

כעת ניישם מסנן תגובה בצד השרת אשר רק יוסיף כותרת חדשה לתגובה.

לעשות את זה, המסנן שלנו צריך ליישם את ContainerResponseFilter מִמְשָׁק וליישם את השיטה היחידה שלה:

@Provider מחלקה ציבורית ResponseServerFilter מיישם ContainerResponseFilter {@Override public void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) זורק IOException {responseContext.getHeaders (). הוסף ("X-Test", "סינון מבחן"); }}

שימו לב שה- ContainerRequestContext הפרמטר משמש רק כקריאה בלבד - מכיוון שאנחנו כבר מעבדים את התגובה.

2.3. הטמעת פילטר לקוח

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

בואו נראה את זה בפעולה עם מסנן שמוסיף מאפיין לבקשה:

המחלקה הציבורית @Provider RequestClientFilter מיישם את ClientRequestFilter {@Override public void filter (ClientRequestContext requestContext) זורק IOException {requestContext.setProperty ("test", "test filter client request"); }}

בואו ניצור לקוח ג'רסי לבדיקת המסנן הזה:

מעמד ציבורי JerseyClient {מחרוזת סטטית פרטית URI_GREETINGS = "// localhost: 8080 / jersey / greetings"; מחרוזת סטטית ציבורית getHelloGreeting () {return createClient (). target (URI_GREETINGS) .request () .get (String.class); } לקוח סטטי פרטי createClient () {ClientConfig config = ClientConfig חדש (); config.register (RequestClientFilter.class); להחזיר את ClientBuilder.newClient (config); }}

שימו לב שעלינו להוסיף את המסנן לתצורת הלקוח כדי לרשום אותו.

לבסוף, ניצור גם מסנן לתגובה אצל הלקוח.

זה עובד בצורה דומה מאוד לזו שבשרת, אך מיישמת את ClientResponseFilter מִמְשָׁק:

@Provider בכיתה ציבורית ResponseClientFilter מיישם את ClientResponseFilter {@Override public void filter (ClientRequestContext requestContext, ClientResponseContext responseContext) זורק IOException {responseContext.getHeaders () .add ("X-Test-Client", "Test filter client client"); }}

שוב, ה ClientRequestContext מיועד למטרות קריאה בלבד.

4. מיירטים

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

זכור זאת הם מבוצעים לאחר המסננים ורק אם קיים גוף הודעה.

ישנם שני סוגים של מיירטים: ReaderInterceptor ו WriterInterceptor, והם זהים גם לשרת וגם לצד הלקוח.

לאחר מכן, אנו ניצור משאב נוסף בשרת שלנו - אליו ניגשת באמצעות POST ומקבלת פרמטר בגוף, כך שמיירטים יבוצעו בעת גישה אליו:

@POST @ Path ("/ custom") תגובה ציבורית getCustomGreeting (שם מחרוזת) {return Response.status (Status.OK.getStatusCode ()) .build (); }

נוסיף גם שיטה חדשה ללקוח ג'רזי שלנו - כדי לבדוק את המשאב החדש הזה:

תגובה סטטית ציבורית getCustomGreeting () {return createClient (). target (URI_GREETINGS + "/ custom") .request () .post (Entity.text ("custom")); }

4.1. יישום א ReaderInterceptor

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

בואו ליצור מיירט בצד השרת כדי לכתוב הודעה מותאמת אישית בגוף הבקשה שיורטה:

המחלקה הציבורית @Provider RequestServerReaderInterceptor מיישמת את ReaderInterceptor {@Override public Object aroundReadFrom (הקשר ReaderInterceptorContext) זורק IOException, WebApplicationException {InputStream is = context.getInputStream (); גוף מחרוזת = BufferedReader חדש (InputStreamReader חדש (הוא)). שורות () .collect (Collectors.joining ("\ n")); context.setInputStream (ByteArrayInputStream חדש ((הודעת גוף + "נוספה במיירט קורא השרת"). getBytes ())); להחזיר context.proceed (); }}

שים לב ש עלינו להתקשר ל להמשיך() שיטהלהתקשר למיירט הבא בשרשרת. לאחר ביצוע כל המיירטים, ייקרא קורא גוף ההודעות המתאים.

3.2. יישום א WriterInterceptor

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

בואו ליצור מיירט כותב כדי להוסיף הודעה לבקשה, בצד הלקוח:

המחלקה הציבורית @Provider RequestClientWriterInterceptor מיישמת את WriterInterceptor {@Override public void aroundWriteTo (WriterInterceptorContext context) זורק IOException, WebApplicationException {context.getOutputStream () .write (("הודעה שנוספה במיירט הסופר בצד הלקוח");). context.proceed (); }}

שוב, עלינו לקרוא לשיטה להמשיך() להתקשר למיירט הבא.

כאשר כל המיירטים יוצאו להורג, ייקרא כותב גוף ההודעות המתאים.

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

לקוח סטטי פרטי createClient () {ClientConfig config = ClientConfig חדש (); config.register (RequestClientFilter.class); config.register (RequestWriterInterceptor.class); להחזיר את ClientBuilder.newClient (config); }

5. צו ביצוע

בואו נסכם את כל מה שראינו עד כה בתרשים שמראה מתי המסננים והמיירטים מבוצעים במהלך בקשה מלקוח לשרת:

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

אם נסתכל על המסננים והמיירטים שיצרנו, הם יבוצעו בסדר הבא:

  1. RequestClientFilter
  2. RequestClientWriterInterceptor
  3. PrematchingRequestFilter
  4. RestrictedOperationsRequestFilter
  5. RequestServerReaderInterceptor
  6. ResponseServerFilter
  7. ResponseClientFilter

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

העדיפות מוגדרת באמצעות מספר שלם וממיין את המסננים והמיירטים בסדר עולה לבקשות ובסדר יורד לתגובות.

בואו נוסיף עדיפות שלנו RestrictedOperationsRequestFilter:

@Provider @Priority (Priorities.AUTHORIZATION) מעמד ציבורי RestrictedOperationsRequestFilter מיישם ContainerRequestFilter {// ...}

שים לב שהשתמשנו בעדיפות מוגדרת מראש למטרות הרשאה.

6. שם מחייב

המסננים והמיירטים שראינו עד כה נקראים גלובליים מכיוון שהם מבוצעים לכל בקשה ותגובה.

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

6.1. כריכה סטטית

אחת הדרכים לבצע את קשירת השם היא באופן סטטי על ידי יצירת הערה מסוימת שתשמש במשאב הרצוי. הערה זו צריכה לכלול את @NameBinding מטה-ביאור.

בואו ניצור אחד ביישום שלנו:

@NameBinding @Retention (RetentionPolicy.RUNTIME) ציבורי @ ממשק HelloBinding {}

לאחר מכן, אנו יכולים להעלות הערות על כמה משאבים @HelloBinding ביאור:

@GET @HelloBinding מחרוזת ציבורית getHelloGreeting () {החזירו "שלום"; }

לבסוף, אנו מציינים גם את אחד מהסננים שלנו עם ההערה הזו, ולכן המסנן הזה יבוצע רק לבקשות ותגובות שניגשות אל getHelloGreeting () שיטה:

@Provider @Priority (Priorities.AUTHORIZATION) @HelloBinding class class RestrictedOperationsRequestFilter מיישם ContainerRequestFilter {// ...}

זכור כי שלנו RestrictedOperationsRequestFilter לא יופעל יותר עבור שאר המשאבים.

6.2. כריכה דינמית

דרך נוספת לעשות זאת היא באמצעות כריכה דינמית, הנטענת בתצורה במהלך ההפעלה.

תחילה נוסיף משאב נוסף לשרת שלנו עבור קטע זה:

@GET @Path ("/ hi") מחרוזת ציבורית ציבורי getHiGreeting () {להחזיר "היי"; }

כעת, בואו ניצור מחייב למשאב זה על ידי יישום ה- DynamicFeature מִמְשָׁק:

המחלקה הציבורית @Provider HelloDynamicBinding מיישמת את DynamicFeature {@Override public void configure (ResourceInfo resourceInfo, FeatureContext context) {if (Greetings.class.equals (resourceInfo.getResourceClass ()) && resourceInfo.getResourceMethod (). GetName (). מכיל ("Hi ")) {context.register (ResponseServerFilter.class); }}}

במקרה זה אנו משייכים את ה- getHiGreeting () שיטה ל ResponseServerFilter שיצרנו קודם.

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

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

7. מסקנה

במדריך זה התמקדנו בהבנת האופן שבו פילטרים ומיירטים עובדים בג'רזי 2 וכיצד נוכל להשתמש בהם ביישום אינטרנט.

כמו תמיד, קוד המקור המלא של הדוגמאות זמין ב- GitHub.


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