Jersey-filtre og avlyttere

1. Introduksjon

I denne artikkelen skal vi forklare hvordan filtre og avskjærere fungerer i Jersey-rammeverket, samt hovedforskjellene mellom disse.

Vi bruker Jersey 2 her, og vi tester applikasjonen vår ved hjelp av en Tomcat 9-server.

2. Oppsett av program

La oss først lage en enkel ressurs på serveren vår:

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

La oss også lage den tilsvarende serverkonfigurasjonen for applikasjonen vår:

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

Hvis du vil grave dypere inn i hvordan du lager en API med Jersey, kan du sjekke ut denne artikkelen.

Du kan også ta en titt på vår klientfokuserte artikkel og lære hvordan du lager en Java-klient med Jersey.

3. Filtre

La oss komme i gang med filtre.

For å si det enkelt, filtre lar oss endre egenskapene til forespørsler og svar - for eksempel HTTP-overskrifter. Filtre kan brukes både på server og klientside.

Husk det filtre kjøres alltid, uavhengig av om ressursen ble funnet eller ikke.

3.1. Implementering av et Request Server Filter

La oss starte med filtrene på serversiden og lage et forespørselsfilter.

Vi gjør det ved å implementere ContainerRequestFilter grensesnitt og registrere det som en Forsørger på serveren vår:

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

Dette enkle filteret avviser bare forespørslene med språket “EN” i forespørselen ved å ringe abortWith () metode.

Som eksemplet viser, måtte vi bare implementere en metode som mottar konteksten til forespørselen, som vi kan endre etter behov.

La oss huske på det dette filteret kjøres etter at ressursen ble matchet.

I tilfelle vi ønsker å utføre et filter før ressursmatchingen, vi kan bruke et forhåndsmatchende filter ved å kommentere filteret vårt med @PreMatching kommentar:

@Provider @ PreMatching public class PrematchingRequestFilter implementerer ContainerRequestFilter {@Override public void filter (ContainerRequestContext ctx) kaster IOException {if (ctx.getMethod (). Tilsvarer ("DELETE")) {LOG.info ("\"; " }}}

Hvis vi prøver å få tilgang til ressursen vår nå, kan vi sjekke at vårt forhåndsmatchende filter utføres først:

2018-02-25 16: 07: 27,800 [http-nio-8080-exec-3] INFO cbjsfPrematchingRequestFilter - prematching filter 2018-02-25 16: 07: 27,816 [http-nio-8080-exec-3] INFO cbjsf RestrictedOperationsRequestFilter - Begrenset operasjonsfilter

3.2. Implementering av et svarserverfilter

Vi implementerer nå et responsfilter på serversiden som bare vil legge til en ny overskrift i svaret.

Å gjøre det, filteret vårt må implementere ContainerResponseFilter grensesnitt og implementere den eneste metoden:

@Provider offentlig klasse ResponseServerFilter implementerer ContainerResponseFilter {@Override public void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) kaster IOException {responsContext.getHeaders (). Legg til ("X-Test", "Filter test"); }}

Legg merke til at ContainerRequestContext parameteren brukes bare som skrivebeskyttet - siden vi allerede behandler svaret.

2.3. Implementering av et klientfilter

Vi jobber nå med filtre på klientsiden. Disse filtrene fungerer på samme måte som serverfiltre, og grensesnittene vi må implementere ligner veldig på serverne.

La oss se det i aksjon med et filter som legger til en egenskap i forespørselen:

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

La oss også opprette en Jersey-klient for å teste dette filteret:

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); }}

Legg merke til at vi må legge til filteret i klientkonfigurasjonen for å registrere det.

Til slutt vil vi også lage et filter for svaret i klienten.

Dette fungerer på en veldig lignende måte som den på serveren, men implementerer ClientResponseFilter grensesnitt:

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

Igjen, den ClientRequestContext er for skrivebeskyttet formål.

4. Avskjærere

Interceptors er mer forbundet med marshalling og unmarshalling av HTTP-meldingslegemer som er inkludert i forespørslene og svarene. De kan brukes både på serveren og på klientsiden.

Husk det de utføres etter filtrene, og bare hvis det er en meldingstekst.

Det er to typer avskjærere: ReaderInterceptor og WriterInterceptor, og de er de samme for både serveren og klientsiden.

Deretter skal vi opprette en annen ressurs på serveren vår - som er tilgjengelig via en POST og mottar en parameter i kroppen, slik at interceptors blir utført når du får tilgang til den:

@POST @Path ("/ custom") offentlig Svar getCustomGreeting (strengnavn) {return Response.status (Status.OK.getStatusCode ()) .build (); }

Vi vil også legge til en ny metode for Jersey-klienten vår - for å teste denne nye ressursen:

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

4.1. Implementering a ReaderInterceptor

Leseravskjærere lar oss manipulere innkommende strømmer, slik at vi kan bruke dem til å endre forespørselen på serversiden eller svaret på klientsiden.

La oss opprette en interceptor på serversiden for å skrive en tilpasset melding i brødteksten til forespørselen som er fanget opp:

@Provider offentlig klasse RequestServerReaderInterceptor implementerer ReaderInterceptor {@Override public Object aroundReadFrom (ReaderInterceptorContext context) kaster IOException, WebApplicationException {InputStream is = context.getInputStream (); Strengtekst = ny BufferedReader (ny InputStreamReader (er)). Linjer () .collect (Collectors.joining ("\ n")); context.setInputStream (ny ByteArrayInputStream ((body + "melding lagt til i serverleseravlytter"). getBytes ())); returner context.proceed (); }}

Legg merke til det vi må kalle fortsette() metodeå ringe neste avlytter i kjeden. Når alle avlytterne er henrettet, vil den aktuelle meldingslegeleseren ringes.

3.2. Implementering a WriterInterceptor

Skribentavskjærere fungerer på en veldig lignende måte som leseravskjærere, men de manipulerer de utgående strømmer - slik at vi kan bruke dem med forespørselen på klientsiden eller med svaret på serversiden.

La oss lage en forfatterinterceptor for å legge til en melding i forespørselen, på klientsiden:

@Provider offentlig klasse RequestClientWriterInterceptor implementerer WriterInterceptor {@Override public void aroundWriteTo (WriterInterceptorContext context) kaster IOException, WebApplicationException {context.getOutputStream () .write (("Melding lagt til forfatterinterceptoren på klientsiden)). context.proceed (); }}

Igjen, vi må kalle metoden fortsette() å ringe neste avlytter.

Når alle avlytterne blir henrettet, blir den aktuelle forfatteren av meldingsorganet ringt.

Ikke glem at du må registrere denne interceptoren i klientkonfigurasjonen, 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. Utførelsespålegg

La oss oppsummere alt vi har sett så langt i et diagram som viser når filtrene og avlytterne blir utført under en forespørsel fra en klient til en server:

Som vi kan se, filtrene blir alltid utført først, og avskjærerne blir utført rett før de ringer til den aktuelle meldingslegemeleseren eller forfatteren.

Hvis vi tar en titt på filtrene og interceptorene vi har opprettet, vil de bli utført i følgende rekkefølge:

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

Videre, når vi har flere filtre eller avlyttere, kan vi spesifisere den eksakte utføringsrekkefølgen ved å kommentere dem med @Prioritet kommentar.

Prioriteten er spesifisert med en Heltall og sorterer filtrene og avskjærerne i stigende rekkefølge for forespørslene og i synkende rekkefølge for svarene.

La oss legge til en prioritering i vår RestrictedOperationsRequestFilter:

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

Legg merke til at vi har brukt en forhåndsdefinert prioritet for autorisasjonsformål.

6. Navn bindende

Filtrene og interceptorene vi har sett så langt kalles globale fordi de utføres for hver forespørsel og respons.

Derimot, de kan også defineres slik at de bare skal kjøres for spesifikke ressursmetoder, som kalles navnebinding.

6.1. Statisk binding

En måte å gjøre navnebindingen på er statisk ved å opprette en bestemt kommentar som skal brukes i ønsket ressurs. Denne merknaden må omfatte @NameBinding meta-kommentar.

La oss lage en i applikasjonen vår:

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

Etter det kan vi kommentere noen ressurser med dette @HelloBinding kommentar:

@GET @HelloBinding public String getHelloGreeting () {return "hallo"; }

Til slutt skal vi også kommentere et av filtrene våre med denne merknaden, så dette filteret vil bare bli utført for forespørsler og svar som får tilgang til getHelloGreeting () metode:

@Provider @Priority (Priorities.AUTHORIZATION) @HelloBinding public class RestrictedOperationsRequestFilter implementerer ContainerRequestFilter {// ...}

Husk at vår RestrictedOperationsRequestFilter vil ikke bli utløst for resten av ressursene lenger.

6.2. Dynamisk binding

En annen måte å gjøre dette på er ved å bruke en dynamisk binding, som lastes inn i konfigurasjonen under oppstart.

La oss først legge til en annen ressurs på serveren vår for denne delen:

@GET @Path ("/ hi") offentlig streng getHiGreeting () {return "hei"; }

La oss nå lage en binding for denne ressursen ved å implementere DynamicFeature grensesnitt:

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

I dette tilfellet forbinder vi getHiGreeting () metoden til ResponseServerFilter som vi hadde skapt før.

Det er viktig å huske at vi måtte slette @Forsørger kommentar fra dette filteret siden vi nå konfigurerer det via DynamicFeature.

Hvis vi ikke gjør dette, vil filteret kjøres to ganger: en gang som et globalt filter og en annen gang som et filter bundet til getHiGreeting () metode.

7. Konklusjon

I denne opplæringen fokuserte vi på å forstå hvordan filtre og interceptors fungerer i Jersey 2 og hvordan vi kan bruke dem i et webapplikasjon.

Som alltid er hele kildekoden for eksemplene tilgjengelig på GitHub.


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