Jersey filtre a zachytávače

1. Úvod

V tomto článku vysvetlíme, ako fungujú filtre a zachytávače v rámci Jersey, ako aj hlavné rozdiely medzi nimi.

Použijeme tu Jersey 2 a našu aplikáciu otestujeme pomocou servera Tomcat 9.

2. Nastavenie aplikácie

Najprv si vytvoríme jednoduchý zdroj na našom serveri:

@Path ("/ greetings") public class Greetings {@GET public String getHelloGreeting () {return "ahoj"; }}

Vytvorme tiež zodpovedajúcu konfiguráciu servera pre našu aplikáciu:

@ApplicationPath ("/ *") verejná trieda ServerConfig rozširuje ResourceConfig {public ServerConfig () {packages ("com.baeldung.jersey.server"); }}

Ak sa chcete hlbšie zaoberať tým, ako vytvoriť API s Jersey, môžete si prečítať tento článok.

Môžete sa tiež pozrieť na náš článok zameraný na klienta a naučiť sa, ako vytvoriť klienta Java pomocou Jersey.

3. Filtre

Teraz začnime s filtrami.

Jednoducho povedané, filtre umožňujú upraviť vlastnosti požiadaviek a odpovedí - napríklad hlavičky HTTP. Filtre je možné použiť na serverovej aj klientskej strane.

Pamätajte na to filtre sa vykonávajú vždy, bez ohľadu na to, či bol zdroj nájdený alebo nie.

3.1. Implementácia filtra servera požiadaviek

Začnime s filtrami na strane servera a vytvorme filter požiadaviek.

Urobíme to implementáciou ContainerRequestFilter rozhranie a zaregistruje sa ako Poskytovateľ na našom serveri:

@Provider verejná trieda RestrictedOperationsRequestFilter implementuje ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) hodí IOException {if (ctx.getLanguage ()! = Null && "EN" .equals (ctx.getLanguage () .getLanguage () .getLanguage () .getLanguage ()) .abortWith (Response.status (Response.Status.FORBIDDEN) .entity ("Nemám prístup") .build ()); }}}

Tento jednoduchý filter iba odmietne žiadosti s jazykom „SK“ v žiadosti zavolaním na abortWith () metóda.

Ako ukazuje príklad, museli sme implementovať iba jednu metódu, ktorá prijíma kontext požiadavky, ktorú môžeme podľa potreby upravovať.

Pamätajme na to tento filter sa vykoná po priradení zdroja.

V prípade, že chceme vykonať filtrovanie pred priradením zdroja, môžeme použiť vopred vyhovujúci filter anotáciou nášho filtra pomocou @PreMatching anotácia:

@Provider @PreMatching verejná trieda PrematchingRequestFilter implementuje ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) hodí IOException {if (ctx.getMethod (). Equals ("DELETE")) {LOG.info ("\" Vymazanie požiadavky "); }}}

Ak sa teraz pokúsime získať prístup k nášmu zdroju, môžeme skontrolovať, či je najskôr vykonaný náš filter predbežnej zhody:

2018-02-25 16: 07: 27,800 [http-nio-8080-exec-3] INFO cbjsfPrematchingRequestFilter - predzberný filter 2018-02-25 16: 07: 27,816 [http-nio-8080-exec-3] INFO cbjsf RestrictedOperationsRequestFilter - filter obmedzených operácií

3.2. Implementácia filtra servera odpovedí

Teraz na strane servera implementujeme filter odpovedí, ktorý do odpovede pridá iba novú hlavičku.

Urobiť to, náš filter musí implementovať ContainerResponseFilter rozhranie a implementovať jeho jedinú metódu:

@Provider verejná trieda ResponseServerFilter implementuje ContainerResponseFilter {@Override filter verejných prázdnin (ContainerRequestContext requestContext, ContainerResponseContext responseContext) hodí IOException {responseContext.getHeaders (). Add ("X-Test", "Filter test", }}

Všimnite si, že ContainerRequestContext parameter sa používa iba na čítanie - pretože odpoveď už spracovávame.

2.3. Implementácia klientskeho filtra

Teraz budeme pracovať s filtrami na strane klienta. Tieto filtre fungujú rovnako ako filtre servera a rozhrania, ktoré musíme implementovať, sú veľmi podobné rozhraniam na strane servera.

Pozrime sa na to v akcii s filtrom, ktorý do žiadosti pridá vlastnosť:

@Provider verejná trieda RequestClientFilter implementuje ClientRequestFilter {@Override public void filter (ClientRequestContext requestContext) hodí IOException {requestContext.setProperty ("test", "testovací filter požiadaviek klienta"); }}

Vytvorme tiež klienta Jersey na otestovanie tohto filtra:

verejná trieda JerseyClient {súkromný statický reťazec URI_GREETINGS = "// localhost: 8080 / dres / pozdravy"; public static String getHelloGreeting () {return createClient (). target (URI_GREETINGS) .request () .get (String.class); } súkromný statický klient createClient () {ClientConfig config = nový ClientConfig (); config.register (RequestClientFilter.class); vrátiť ClientBuilder.newClient (konfigurácia); }}

Všimnite si, že musíme pridať filter do konfigurácie klienta, aby sme ho zaregistrovali.

Nakoniec tiež vytvoríme filter pre odpoveď v klientovi.

Funguje to veľmi podobným spôsobom ako na serveri, ale implementácia ClientResponseFilter rozhranie:

@Provider verejná trieda ResponseClientFilter implementuje ClientResponseFilter {@Override verejný filter prázdnych miest (ClientRequestContext requestContext, ClientResponseContext responseContext) hodí IOException {responseContext.getHeaders () .add ("X-Test-Client", "Test klientskeho filtra"); }}

Opäť platí, že ClientRequestContext je iba na čítanie.

4. Interceptory

Interceptory sú viac spojené s radením a oddelovaním tiel správ HTTP, ktoré sú obsiahnuté v požiadavkách a odpovediach. Môžu byť použité na serveri aj na strane klienta.

Pamätajte na to vykonajú sa po filtroch a iba ak je prítomné telo správy.

Existujú dva typy zachytávačov: ReaderInterceptor a WriterInterceptora sú rovnaké pre serverovú aj klientskú stranu.

Ďalej na našom serveri vytvoríme ďalší zdroj - ktorý je prístupný prostredníctvom POST a prijíma parameter v tele, takže pri prístupe k nemu sa vykonajú zachytávače:

@POST @Path ("/ custom") public Response getCustomGreeting (názov reťazca) {return Response.status (Status.OK.getStatusCode ()) .build (); }

Do nášho klienta v Jersey pridáme aj novú metódu - na otestovanie tohto nového zdroja:

public static Response getCustomGreeting () {return createClient (). target (URI_GREETINGS + "/ custom") .request () .post (Entity.text ("custom")); }

4.1. Implementácia a ReaderInterceptor

Zachytávače čítačiek nám umožňujú manipulovať s prichádzajúcimi prúdmi, takže ich môžeme použiť na úpravu požiadavky na strane servera alebo odpovede na strane klienta.

Vytvorme zachytávač na strane servera, ktorý napíše vlastnú správu do tela zachytenej žiadosti:

@Provider verejná trieda RequestServerReaderInterceptor implementuje ReaderInterceptor {@Override verejný objekt aroundReadFrom (kontext ReaderInterceptorContext) hodí IOException, WebApplicationException {InputStream je = context.getInputStream (); Text reťazca = nový BufferedReader (nový InputStreamReader (je)). Lines () .collect (Collectors.joining ("\ n")); context.setInputStream (nový ByteArrayInputStream ((telo + "správa bola pridaná do zachytávača serverových čítačiek"). getBytes ())); návrat context.proceed (); }}

Všimni si musíme zavolať pokračovať () metódazavolať ďalšieho zachytávača v reťazci. Len čo sa vykonajú všetky zachytávače, zavolá sa vhodný čítač tela správy.

3.2. Implementácia a WriterInterceptor

Zachytávače spisovateľov pracujú veľmi podobne ako zachytávače čitateľov, ale manipulujú s odchádzajúcimi prúdmi - takže ich môžeme použiť s požiadavkou na strane klienta alebo s odpoveďou na strane servera.

Vytvorme interceptor spisovateľa na pridanie správy k žiadosti na strane klienta:

@Provider verejná trieda RequestClientWriterInterceptor implementuje WriterInterceptor {@Override public void aroundWriteTo (WriterInterceptorContext context) hodí IOException, WebApplicationException {context.getOutputStream () .write (("Správa pridaná do interceptora spisovateľa na strane klienta").) context.proceed (); }}

Opäť musíme zavolať metódu pokračovať () zavolať ďalšieho zachytávača.

Keď sa vykonajú všetky zachytávače, zavolá sa vhodný zapisovač tela správy.

Nezabudnite, že musíte tento zachytávač zaregistrovať v konfigurácii klienta, ako sme to urobili predtým s filtrom pre klientov:

private static Client createClient () {ClientConfig config = nový ClientConfig (); config.register (RequestClientFilter.class); config.register (RequestWriterInterceptor.class); vrátiť ClientBuilder.newClient (konfigurácia); }

5. Exekučný príkaz

Zhrňme si všetko, čo sme doteraz videli, v diagrame, ktorý ukazuje, kedy sú filtre a zachytávače vykonávané počas požiadavky klienta na server:

Ako vidíme, filtre sa vždy vykonajú ako prvé a zachytávače sa vykonajú tesne pred zavolaním príslušného čítača alebo zapisovača tela správy.

Ak sa pozrieme na filtre a zachytávače, ktoré sme vytvorili, budú vykonané v nasledujúcom poradí:

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

Ďalej, keď máme niekoľko filtrov alebo zachytávačov, môžeme určiť presný vykonávací príkaz ich anotáciou pomocou @ Priorita anotácia.

Priorita je určená znakom Celé číslo a zoradí filtre a zachytávače vzostupne podľa požiadaviek a zostupne podľa odpovedí.

Pridajme prednosť tomu nášmu RestrictedOperationsRequestFilter:

@Provider @Priority (Priorities.AUTHORIZATION) verejná trieda RestrictedOperationsRequestFilter implementuje ContainerRequestFilter {// ...}

Všimnite si, že sme na účely autorizácie použili preddefinovanú prioritu.

6. Väzba mien

Filtre a zachytávače, ktoré sme doteraz videli, sa nazývajú globálne, pretože sa vykonávajú pre každú požiadavku a odpoveď.

Avšak môžu byť tiež definované tak, aby sa vykonávali iba pre konkrétne metódy zdrojov, ktorá sa nazýva väzba mien.

6.1. Statické viazanie

Jedným zo spôsobov, ako vytvoriť väzbu názvu, je staticky vytvorením konkrétnej anotácie, ktorá sa použije v požadovanom prostriedku. Táto anotácia musí obsahovať @NameBinding meta-anotácia.

Vytvorme si jeden v našej aplikácii:

@NameBinding @Retention (RetentionPolicy.RUNTIME) verejné @interface HelloBinding {}

Potom s tým môžeme anotovať niektoré zdroje @HelloBinding anotácia:

@GET @HelloBinding public String getHelloGreeting () {návrat "ahoj"; }

Nakoniec budeme tiež anotovať jeden z našich filtrov touto anotáciou, takže tento filter sa vykoná iba pre žiadosti a odpovede, ktoré pristupujú k getHelloGreeting () metóda:

@Provider @Priority (Priorities.AUTHORIZATION) @HelloBinding verejná trieda RestrictedOperationsRequestFilter implementuje ContainerRequestFilter {// ...}

Majte na pamäti, že naše RestrictedOperationsRequestFilter sa pre ostatné zdroje už nespustí.

6.2. Dynamická väzba

Ďalším spôsobom, ako to urobiť, je použitie dynamickej väzby, ktorá sa načíta v konfigurácii počas spustenia.

Najprv pre túto časť pridáme na náš server ďalší zdroj:

@GET @Path ("/ hi") public String getHiGreeting () {return "ahoj"; }

Teraz vytvorme väzbu pre tento zdroj implementáciou DynamicFeature rozhranie:

@Provider verejná trieda HelloDynamicBinding implementuje DynamicFeature {@Override public void configure (ResourceInfo resourceInfo, FeatureContext context) {if (Greetings.class.equals (resourceInfo.getResourceClass ()) && resourceInfo.getResourceMethod (). GetName (). Contains ("HiGreeting" ")) {context.register (ResponseServerFilter.class); }}}

V tomto prípade združujeme getHiGreeting () metóda do ResponseServerFilter ktoré sme vytvorili predtým.

Je dôležité mať na pamäti, že sme museli vymazať @ Poskytovateľ anotáciu z tohto filtra, pretože ju teraz konfigurujeme pomocou DynamicFeature.

Ak to neurobíme, filter sa vykoná dvakrát: jedenkrát ako globálny filter a druhýkrát ako filter viazaný na getHiGreeting () metóda.

7. Záver

V tomto tutoriáli sme sa zamerali na pochopenie toho, ako filtre a zachytávače fungujú v Jersey 2 a ako ich môžeme použiť vo webovej aplikácii.

Úplný zdrojový kód príkladov je ako vždy k dispozícii na serveri GitHub.