Jersey filtri i presretači

1. Uvod

U ovom ćemo članku objasniti kako filtri i presretači rade u okviru Jerseyja, kao i glavne razlike između njih.

Ovdje ćemo upotrijebiti Jersey 2, a našu ćemo aplikaciju testirati pomoću poslužitelja Tomcat 9.

2. Postavljanje aplikacije

Stvorimo najprije jednostavan resurs na našem poslužitelju:

@Path ("/ greetings") pozdrav javne klase {@GET public String getHelloGreeting () {return "hello"; }}

Također, kreirajmo odgovarajuću konfiguraciju poslužitelja za našu aplikaciju:

@ApplicationPath ("/ *") javna klasa ServerConfig proširuje ResourceConfig {public ServerConfig () {paketi ("com.baeldung.jersey.server"); }}

Ako želite dublje istražiti kako stvoriti API s Jerseyem, možete pogledati ovaj članak.

Također možete pogledati naš članak usmjeren na klijenta i naučiti kako stvoriti Java klijent s Jerseyem.

3. Filteri

Sada, krenimo s filtrima.

Jednostavno rečeno, filtri dopuštaju izmjenu svojstava zahtjeva i odgovora - na primjer, HTTP zaglavlja. Filteri se mogu primijeniti na poslužitelju i na strani klijenta.

Imajte na umu da filtri se uvijek izvršavaju, bez obzira je li resurs pronađen ili ne.

3.1. Implementacija filtra poslužitelja zahtjeva

Počnimo s filtrima na strani poslužitelja i stvorimo filtar zahtjeva.

To ćemo učiniti primjenom ContainerRequestFilter sučelje i registriranje kao Pružatelj usluga na našem poslužitelju:

Javna klasa @Provider RestrictedOperationsRequestFilter implementira ContainerRequestFilter {@Override javni filtar za prazninu (ContainerRequestContext ctx) baca IOException {if (ctx.getLanguage ()! = Null && "EN" .equals (ctx.getgetText). .abortWith (Response.status (Response.Status.FORBIDDEN) .entity ("Ne može pristupiti"). build ()); }}}

Ovaj jednostavni filtar samo odbija zahtjeve s jezikom "EN" u zahtjevu pozivom na abortWith () metoda.

Kao što primjer pokazuje, morali smo implementirati samo jednu metodu koja prima kontekst zahtjeva, a koju možemo izmijeniti prema potrebi.

Imajmo to na umu ovaj se filtar izvršava nakon podudaranja resursa.

U slučaju da želimo izvršiti filtar prije podudaranja resursa, možemo upotrijebiti filtar za prethodno podudaranje označavanjem filtra s @PreMatching bilješka:

@Provider @PreMatching javne klase PrematchingRequestFilter implementira ContainerRequestFilter {@Override javni void filter (ContainerRequestContext ctx) baca IOException {if (ctx.getMethod (). Equals ("DELETE")) {LOG.info "(" \ "Delete" }}}

Ako sada pokušamo pristupiti našem resursu, možemo provjeriti je li najprije izvršen naš filter za podudaranje:

2018-02-25 16: 07: 27,800 [http-nio-8080-exec-3] INFO cbjsfPrematchingRequestFilter - filtar za predusklađivanje 2018-02-25 16: 07: 27,816 [http-nio-8080-exec-3] INFO cbjsf RestrictedOperationsRequestFilter - filtar ograničenih operacija

3.2. Implementacija filtra poslužitelja odgovora

Sada ćemo implementirati filtar odgovora na strani poslužitelja koji će odgovoru samo dodati novo zaglavlje.

Napraviti to, naš filtar mora implementirati ContainerResponseFilter sučelje i implementirati svoju jedinu metodu:

@Provider javna klasa ResponseServerFilter implementira ContainerResponseFilter {@Override javni void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) baca IOException {responseContext.getHeaders (). Test; "test," test, "Test," X, Test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test, "test," test "); }}

Primijetite da ContainerRequestContext parametar samo se koristi kao samo za čitanje - budući da već obrađujemo odgovor.

2.3. Implementacija klijentskog filtra

Sad ćemo raditi s filtrima na strani klijenta. Ti filtri rade na isti način kao i poslužiteljski filtri, a sučelja koja moramo implementirati vrlo su slična onima na poslužiteljskoj strani.

Pogledajmo to na djelu s filtrom koji zahtjevu dodaje svojstvo:

@Provider javna klasa RequestClientFilter implementira ClientRequestFilter {@Override javni void filter (ClientRequestContext requestContext) baca IOException {requestContext.setProperty ("test", "test client client filter"); }}

Stvorimo i Jersey klijenta za testiranje ovog filtra:

javna klasa JerseyClient {private static String URI_GREETINGS = "// localhost: 8080 / jersey / greetings"; javni statički niz getHelloGreeting () {return createClient (). target (URI_GREETINGS) .request () .get (String.class); } privatni statički klijent createClient () {ClientConfig config = novi ClientConfig (); config.register (RequestClientFilter.class); vratiti ClientBuilder.newClient (config); }}

Primijetite da filtar moramo dodati u konfiguraciju klijenta da bismo ga registrirali.

Na kraju ćemo stvoriti i filtar za odgovor u klijentu.

Ovo radi na vrlo sličan način kao onaj na poslužitelju, ali implementira ClientResponseFilter sučelje:

@Provider javna klasa ResponseClientFilter implementira ClientResponseFilter {@Override javni filtar za prazninu (ClientRequestContext requestContext, ClientResponseContext responseContext) baca IOException {responseContext.getHeaders (). Test-"ClientResponseFilter"); }}

Opet, ClientRequestContext je namijenjen samo za čitanje.

4. Presretači

Presretači su više povezani s razvrstavanjem i uklanjanjem oznaka tijela HTTP poruka koja su sadržana u zahtjevima i odgovorima. Mogu se koristiti i na poslužitelju i na klijentskoj strani.

Imajte na umu da izvršavaju se nakon filtara i samo ako je prisutno tijelo poruke.

Postoje dvije vrste presretača: ReaderInterceptor i WriterInterceptor, a jednaki su i za poslužitelja i za klijenta.

Dalje, stvorit ćemo još jedan resurs na našem poslužitelju - kojem se pristupa putem POST-a i prima parametar u tijelu, pa će se presretači izvršavati prilikom pristupa njemu:

@POST @Path ("/ custom") javni odgovor getCustomGreeting (naziv niza) {return Response.status (Status.OK.getStatusCode ()) .build (); }

Također ćemo dodati novu metodu našem klijentu iz Jerseyja - kako bismo testirali ovaj novi resurs:

javni statički odgovor getCustomGreeting () {return createClient (). target (URI_GREETINGS + "/ custom") .request () .post (Entity.text ("custom")); }

4.1. Provedba a ReaderInterceptor

Čitač-presretači omogućuju nam manipulaciju ulaznim tokovima, pa ih možemo koristiti za izmjenu zahtjeva na strani poslužitelja ili odgovora na strani klijenta.

Stvorimo presretač na strani poslužitelja za pisanje prilagođene poruke u tijelu presretnutog zahtjeva:

@Provider javna klasa RequestServerReaderInterceptor implementira ReaderInterceptor {@Override javni objekt okoReadFrom (kontekst ReaderInterceptorContext) baca IOException, WebApplicationException {InputStream is = context.getInputStream (); Tijelo niza = novi BufferedReader (novi InputStreamReader (je)). Lines () .collect (Collectors.joining ("\ n")); context.setInputStream (novi ByteArrayInputStream ((body + "poruka dodana u presretaču čitača poslužitelja"). getBytes ())); vratiti kontekst.proceed (); }}

Primijeti da moramo nazvati nastavi () metodada pozove sljedećeg presretača u lancu. Nakon što se izvrše svi presretači, pozvat će se odgovarajući čitač tijela poruke.

3.2. Provedba a WriterInterceptor

Presretači Writer rade na vrlo sličan način kao presretači čitača, ali manipuliraju izlaznim tokovima - tako da ih možemo koristiti sa zahtjevom na klijentskoj strani ili s odgovorom na poslužiteljskoj strani.

Stvorimo presretač pisca da na zahtjev klijenta dodamo poruku:

@Provider javna klasa RequestClientWriterInterceptor implementira WriterInterceptor {@Override public void aroundWriteTo (WriterInterceptorContext context) baca IOException, WebApplicationException {context.getOutputStream () .write ((") poruka dodana u clientB intertesor. context.proceed (); }}

Opet moramo pozvati metodu nastavi () da pozove sljedećeg presretača.

Kada se svi presretači izvrše, pozvat će se odgovarajući program za pisanje tijela poruke.

Ne zaboravite da ovaj presretač morate registrirati u konfiguraciji klijenta, kao što smo prije radili s klijentskim filtrom:

privatni statički klijent createClient () {ClientConfig config = novi ClientConfig (); config.register (RequestClientFilter.class); config.register (RequestWriterInterceptor.class); vratiti ClientBuilder.newClient (config); }

5. Nalog za izvršenje

Sažmimo sve što smo do sada vidjeli u dijagramu koji pokazuje kada se filtri i presretači izvršavaju tijekom zahtjeva klijenta poslužitelju:

Kao što vidimo, filtri se uvijek izvršavaju prvo, a presretači se izvršavaju neposredno prije poziva odgovarajućeg čitača ili pisca tijela poruke.

Ako pogledamo filtere i presretače koje smo stvorili, oni će se izvršiti sljedećim redoslijedom:

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

Nadalje, kada imamo nekoliko filtara ili presretača, možemo odrediti točan redoslijed izvršenja tako da ih označimo s @Prioritet bilješka.

Prioritet se navodi s Cijeli broj i sortira filtre i presretače u uzlaznom redoslijedu za zahtjeve i u silaznom nizu za odgovore.

Dodajmo prioritet našem RestrictedOperationsRequestFilter:

@Provider @Priority (Priorities.AUTHORIZATION) javna klasa RestrictedOperationsRequestFilter implementira ContainerRequestFilter {// ...}

Primijetite da smo za potrebe autorizacije koristili unaprijed definirani prioritet.

6. Vezivanje imena

Filtri i presretači koje smo do sada vidjeli nazivaju se globalnima, jer se izvršavaju za svaki zahtjev i odgovor.

Međutim, oni se također mogu definirati da se izvršavaju samo za određene metode resursa, što se naziva vezivanje naziva.

6.1. Statičko vezanje

Jedan od načina za vezivanje naziva je statički stvaranjem određene napomene koja će se koristiti u željenom resursu. Ova napomena mora sadržavati @NameBinding meta-anotacija.

Stvorimo jedan u našoj aplikaciji:

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

Nakon toga možemo ovima dodati neke resurse @HelloBinding napomena:

@GET @HelloBinding javni niz getHelloGreeting () {return "zdravo"; }

Konačno, označit ćemo jedan od naših filtara i ovom bilješkom, pa će se ovaj filtar izvršavati samo za zahtjeve i odgovore koji pristupaju getHelloGreeting () metoda:

@Provider @Priority (Priorities.AUTHORIZATION) @HelloBinding javna klasa RestrictedOperationsRequestFilter implementira ContainerRequestFilter {// ...}

Imajte na umu da je naš RestrictedOperationsRequestFilter više se neće aktivirati za ostatak resursa.

6.2. Dinamičko povezivanje

Drugi način da se to učini je upotreba dinamičkog vezanja koje se učitava u konfiguraciji tijekom pokretanja.

Prvo dodamo još jedan resurs na naš poslužitelj za ovaj odjeljak:

@GET @Path ("/ hi") javni niz getHiGreeting () {return "hi"; }

Sada, izradimo obvezujući za ovaj resurs primjenom DynamicFeature sučelje:

Javna klasa @Provider HelloDynamicBinding implementira DynamicFeature {@Override public void configure (ResourceInfo resourceInfo, FeatureContext context) {if (Greetings.class.equals (resourceInfo.getResourceClass ()) && resourceInfo.getResourceMethod (). GetName (Hi). ")) {context.register (ResponseServerFilter.class); }}}

U ovom slučaju povezujemo getHiGreeting () metoda za ResponseServerFilter koje smo prije stvorili.

Važno je zapamtiti da smo morali izbrisati @Provider napomena iz ovog filtra jer ga sada konfiguriramo putem DynamicFeature.

Ako to ne učinimo, filtar će se izvršiti dva puta: jedanput kao globalni, a drugi put kao filtar vezan za getHiGreeting () metoda.

7. Zaključak

U ovom smo se vodiču usredotočili na razumijevanje načina na koji filtri i presretači rade u Jersey 2 i kako ih možemo koristiti u web aplikaciji.

Kao i uvijek, puni izvorni kod za primjere dostupan je na GitHubu.