Jersey-filtre og interceptors

1. Introduktion

I denne artikel vil vi forklare, hvordan filtre og interceptorer fungerer i Jersey-rammen, såvel som de største forskelle mellem disse.

Vi bruger Jersey 2 her, og vi tester vores applikation ved hjælp af en Tomcat 9-server.

2. Opsætning af applikation

Lad os først oprette en simpel ressource på vores server:

@Path ("/ greetings") offentlig klasse hilsner {@GET public String getHelloGreeting () {returner "hej"; }}

Lad os også oprette den tilsvarende serverkonfiguration til vores applikation:

@ApplicationPath ("/ *") offentlig klasse ServerConfig udvider ResourceConfig {public ServerConfig () {pakker ("com.baeldung.jersey.server"); }}

Hvis du vil grave dybere ned i, hvordan du opretter en API med Jersey, kan du tjekke denne artikel.

Du kan også se på vores klientfokuserede artikel og lære at oprette en Java-klient med Jersey.

3. Filtre

Lad os nu komme i gang med filtre.

Kort fortalt, med filtre kan vi ændre egenskaberne for anmodninger og svar - for eksempel HTTP-headere. Filtre kan anvendes både på server- og klientsiden.

Husk det filtre udføres altid, uanset om ressourcen blev fundet eller ej.

3.1. Implementering af et Request Server Filter

Lad os starte med filtrene på serversiden og oprette et anmodningsfilter.

Vi gør det ved at implementere ContainerRequestFilter interface og registrere det som en Udbyder på vores server:

@Provider public class RestrictedOperationsRequestFilter implementerer ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) kaster IOException {if (ctx.getLanguage ()! = Null && "EN" .equals (ctx.getLanguage ().) .abortWith (Response.status (Response.Status.FORBIDDEN) .entity ("Kan ikke få adgang") .build ()); }}}

Dette enkle filter afviser bare anmodningerne med sproget “EN” i anmodningen ved at ringe til afbryde med () metode.

Som eksemplet viser, måtte vi kun implementere en metode, der modtager konteksten for anmodningen, som vi kan ændre efter behov.

Lad os huske det dette filter udføres, efter at ressourcen blev matchet.

I tilfælde af at vi vil udføre et filter inden ressourcematchingen, vi kan bruge et præ-matchende filter ved at kommentere vores filter med @PreMatching kommentar:

@Provider @PreMatching offentlig klasse PrematchingRequestFilter implementerer ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) kaster IOException {if (ctx.getMethod (). Er lig med ("DELETE")) {LOG.info ("\"; " }}}

Hvis vi prøver at få adgang til vores ressource nu, kan vi kontrollere, at vores præ-matching filter udføres først:

2018-02-25 16: 07: 27,800 [http-nio-8080-exec-3] INFO cbjsfPrematchingRequestFilter - forbatchningsfilter 2018-02-25 16: 07: 27,816 [http-nio-8080-exec-3] INFO cbjsf RestrictedOperationsRequestFilter - Filter for begrænsede operationer

3.2. Implementering af et svarserverfilter

Vi implementerer nu et svarfilter på serversiden, der blot tilføjer en ny overskrift til svaret.

At gøre det, vores filter skal implementere ContainerResponseFilter interface og implementere sin eneste metode:

@Provider public class ResponseServerFilter implementerer ContainerResponseFilter {@Override public void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) kaster IOException {responsContext.getHeaders (). Tilføj ("X-Test", "Filter test"); }}

Bemærk, at ContainerRequestContext parameter bruges bare som skrivebeskyttet - da vi allerede behandler svaret.

2.3. Implementering af et klientfilter

Vi arbejder nu med filtre på klientsiden. Disse filtre fungerer på samme måde som serverfiltre, og de grænseflader, vi skal implementere, ligner meget dem på serversiden.

Lad os se det i aktion med et filter, der føjer en egenskab til anmodningen:

@Provider public class RequestClientFilter implementerer ClientRequestFilter {@Override public void filter (ClientRequestContext requestContext) kaster IOException {requestContext.setProperty ("test", "test client request filter"); }}

Lad os også oprette en Jersey-klient til at teste dette filter:

offentlig klasse JerseyClient {privat statisk streng URI_GREETINGS = "// localhost: 8080 / jersey / greetings"; offentlig statisk streng getHelloGreeting () {return createClient (). mål (URI_GREETINGS) .request () .get (String.class); } privat statisk klient createClient () {ClientConfig config = ny ClientConfig (); config.register (RequestClientFilter.class); returner ClientBuilder.newClient (config); }}

Bemærk, at vi skal tilføje filteret til klientkonfigurationen for at registrere det.

Endelig opretter vi også et filter til svaret i klienten.

Dette fungerer på en meget lignende måde som den på serveren, men implementerer ClientResponseFilter grænseflade:

@Provider offentlig klasse ResponseClientFilter implementerer ClientResponseFilter {@Override public void filter (ClientRequestContext requestContext, ClientResponseContext responseContext) kaster IOException {responsContext.getHeaders () .add ("X-Test-Client", "Test respons klientfilter"); }}

Igen, den ClientRequestContext er til skrivebeskyttede formål.

4. Interceptorer

Interceptors er mere forbundet med marshalling og unmarshalling af HTTP-meddelelseslegemer, der er indeholdt i anmodningerne og svarene. De kan bruges både på serveren og på klientsiden.

Husk det de udføres efter filtrene, og kun hvis der er en meddelelsesdel.

Der er to typer aflyttere: ReaderInterceptor og WriterInterceptor, og de er de samme for både serveren og klientsiden.

Dernæst opretter vi en anden ressource på vores server - der er adgang til via en POST og modtager en parameter i kroppen, så interceptors udføres, når de får adgang til den:

@POST @Path ("/ brugerdefineret") offentligt svar getCustomGreeting (strengnavn) {returner Response.status (Status.OK.getStatusCode ()) .build (); }

Vi tilføjer også en ny metode til vores Jersey-klient - for at teste denne nye ressource:

offentlig statisk reaktion getCustomGreeting () {return createClient (). target (URI_GREETINGS + "/ custom") .request () .post (Entity.text ("custom")); }

4.1. Implementering af en ReaderInterceptor

Læserinterceptorer giver os mulighed for at manipulere indgående strømme, så vi kan bruge dem til at ændre anmodningen på serversiden eller svaret på klientsiden.

Lad os oprette en interceptor på serversiden for at skrive en brugerdefineret besked i kroppen af ​​den aflyste anmodning:

@Provider public class RequestServerReaderInterceptor implementerer ReaderInterceptor {@Override public Object aroundReadFrom (ReaderInterceptorContext context) kaster IOException, WebApplicationException {InputStream er = context.getInputStream (); String body = new BufferedReader (new InputStreamReader (is)). Lines () .collect (Collectors.joining ("\ n")); context.setInputStream (ny ByteArrayInputStream ((body + "meddelelse tilføjet i serverlæser interceptor"). getBytes ()); returner context.proceed (); }}

Læg mærke til det vi er nødt til at kalde Fortsæt() metodeat ringe til den næste interceptor i kæden. Når alle interceptors er udført, kaldes den relevante meddelelseslegeme-læser.

3.2. Implementering af en WriterInterceptor

Writer interceptors fungerer på en meget lignende måde som reader interceptors, men de manipulerer de udgående streams - så vi kan bruge dem med anmodningen på klientsiden eller med svaret på serversiden.

Lad os oprette en forfatterinterceptor for at tilføje en besked til anmodningen på klientsiden:

@Provider public class RequestClientWriterInterceptor implementerer WriterInterceptor {@Override public void aroundWriteTo (WriterInterceptorContext context) smider IOException, WebApplicationException {context.getOutputStream () .write (("Besked tilføjet i forfatterinterceptoren på klientsiden)). context.proceed (); }}

Igen er vi nødt til at kalde metoden Fortsæt() at ringe til den næste interceptor.

Når alle aflytterne udføres, kaldes den relevante beskedtekstforfatter.

Glem ikke, at du skal registrere denne interceptor i klientkonfigurationen, som vi gjorde før med klientfilteret:

privat statisk klient createClient () {ClientConfig config = ny ClientConfig (); config.register (RequestClientFilter.class); config.register (RequestWriterInterceptor.class); returner ClientBuilder.newClient (config); }

5. Udførelsesordre

Lad os opsummere alt det, vi hidtil har set i et diagram, der viser, hvornår filtre og interceptors udføres under en anmodning fra en klient til en server:

Som vi kan se, filtrene udføres altid først, og interceptorerne udføres lige inden de ringer til den relevante meddelelseslegeme-læser eller -forfatter.

Hvis vi kigger på de filtre og interceptorer, som vi har oprettet, udføres de i følgende rækkefølge:

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

Når vi desuden har flere filtre eller interceptors, kan vi specificere den nøjagtige udførelsesrækkefølge ved at kommentere dem med @Prioritet kommentar.

Prioriteten er angivet med en Heltal og sorterer filtre og interceptors i stigende rækkefølge for anmodningerne og i faldende rækkefølge for svarene.

Lad os tilføje en prioritet til vores RestrictedOperationsRequestFilter:

@Provider @Priority (Priorities.AUTHORIZATION) offentlig klasse RestrictedOperationsRequestFilter implementerer ContainerRequestFilter {// ...}

Bemærk, at vi har brugt en foruddefineret prioritet til godkendelsesformål.

6. Navn bindende

De filtre og interceptors, vi hidtil har set, kaldes globale, fordi de udføres for hver anmodning og svar.

Imidlertid, de kan også defineres til kun at blive udført for specifikke ressourcemetoder, der kaldes navnebinding.

6.1. Statisk binding

En måde at gøre navnebindingen på er statisk ved at oprette en bestemt kommentar, der vil blive brugt i den ønskede ressource. Denne kommentar skal omfatte @NameBinding meta-kommentar.

Lad os oprette en i vores applikation:

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

Derefter kan vi kommentere nogle ressourcer med dette @HelloBinding kommentar:

@GET @HelloBinding offentlig String getHelloGreeting () {return "hej"; }

Endelig vil vi også kommentere et af vores filtre med denne kommentar, så dette filter udføres kun for anmodninger og svar, der har adgang til getHelloGreeting () metode:

@Provider @Priority (Priorities.AUTHORIZATION) @HelloBinding offentlig klasse RestrictedOperationsRequestFilter implementerer ContainerRequestFilter {// ...}

Husk, at vores RestrictedOperationsRequestFilter udløses ikke for resten af ​​ressourcerne længere.

6.2. Dynamisk binding

En anden måde at gøre dette på er ved hjælp af en dynamisk binding, der indlæses i konfigurationen under opstart.

Lad os først tilføje en anden ressource til vores server til dette afsnit:

@GET @Path ("/ hi") offentlig streng getHiGreeting () {returner "hej"; }

Lad os nu oprette en binding til denne ressource ved at implementere DynamicFeature grænseflade:

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

I dette tilfælde forbinder vi getHiGreeting () metode til ResponseServerFilter som vi havde skabt før.

Det er vigtigt at huske, at vi var nødt til at slette @Udbyder kommentar fra dette filter, da vi nu konfigurerer det via DynamicFeature.

Hvis vi ikke gør dette, udføres filteret to gange: en gang som et globalt filter og en anden gang som et filter bundet til getHiGreeting () metode.

7. Konklusion

I denne vejledning fokuserede vi på at forstå, hvordan filtre og interceptors fungerer i Jersey 2, og hvordan vi kan bruge dem i en webapplikation.

Som altid er den fulde kildekode til eksemplerne tilgængelig på GitHub.


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