Jersey Filters en Interceptors

1. Inleiding

In dit artikel gaan we uitleggen hoe filters en interceptors werken in het Jersey-framework, evenals de belangrijkste verschillen daartussen.

We gebruiken Jersey 2 hier en we zullen onze applicatie testen met een Tomcat 9-server.

2. Applicatie-instellingen

Laten we eerst een eenvoudige bron op onze server maken:

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

Laten we ook de bijbehorende serverconfiguratie voor onze applicatie maken:

@ApplicationPath ("/ *") openbare klasse ServerConfig breidt ResourceConfig {openbare ServerConfig () {pakketten ("com.baeldung.jersey.server") uit; }}

Als je dieper wilt ingaan op het maken van een API met Jersey, kun je dit artikel lezen.

U kunt ook ons ​​klantgerichte artikel bekijken en leren hoe u met Jersey een Java-client maakt.

3. Filters

Laten we nu aan de slag gaan met filters.

Simpel gezegd, Met filters kunnen we de eigenschappen van verzoeken en reacties wijzigen - bijvoorbeeld HTTP-headers. Filters kunnen zowel aan de server- als aan de clientzijde worden toegepast.

Houd daar rekening mee filters worden altijd uitgevoerd, ongeacht of de bron is gevonden of niet.

3.1. Implementeren van een Request Server-filter

Laten we beginnen met de filters aan de serverkant en een verzoekfilter maken.

We zullen dat doen door de implementatie van het ContainerRequestFilter interface en het registreren als een Aanbieder in onze server:

@Provider openbare klasse RestrictedOperationsRequestFilter implementeert ContainerRequestFilter {@Override openbaar ongeldig filter (ContainerRequestContext ctx) gooit IOException {if (ctx.getLanguage ()! = Null && "EN" .equals (ctx.getLanguage () .getLanguage () .getLanguage ()))) {ctx.getLanguage ())) .abortWith (Response.status (Response.Status.FORBIDDEN) .entity ("Geen toegang") .build ()); }}}

Dit eenvoudige filter verwerpt gewoon de verzoeken met de taal "NL" in het verzoek door de abortMet () methode.

Zoals het voorbeeld laat zien, moesten we slechts één methode implementeren die de context van het verzoek ontvangt, die we naar behoefte kunnen aanpassen.

Laten we daar rekening mee houden dit filter wordt uitgevoerd nadat de bron is gematcht.

In het geval dat we een filter willen uitvoeren vóór de resource-matching, we kunnen een pre-matching filter gebruiken door ons filter te annoteren met de @PreMatching annotatie:

@Provider @PreMatching openbare klasse PrematchingRequestFilter implementeert ContainerRequestFilter {@Override openbaar ongeldig filter (ContainerRequestContext ctx) genereert IOException {if (ctx.getMethod (). Equals ("DELETE")) {LOG.info ("\" Verwijderingsverzoek "); }}}

Als we nu proberen toegang te krijgen tot onze bron, kunnen we controleren of ons pre-matching filter eerst wordt uitgevoerd:

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 - Filter voor beperkte bewerkingen

3.2. Implementatie van een antwoordserverfilter

We zullen nu een antwoordfilter aan de serverzijde implementeren dat alleen een nieuwe header aan het antwoord toevoegt.

Om dat te doen, ons filter moet het ContainerResponseFilter koppel en implementeer de enige methode:

@Provider public class ResponseServerFilter implementeert ContainerResponseFilter {@Override public void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) gooit IOException {responseContext.getHeaders (). Add ("X-Test", "Filter test"); }}

Merk op dat de ContainerRequestContext parameter wordt alleen gebruikt als alleen-lezen - aangezien we het antwoord al aan het verwerken zijn.

2.3. Een klantenfilter implementeren

We werken nu met filters aan de kant van de klant. Deze filters werken op dezelfde manier als serverfilters, en de interfaces die we moeten implementeren, lijken sterk op die voor de server.

Laten we het in actie zien met een filter dat een eigenschap aan het verzoek toevoegt:

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

Laten we ook een Jersey-client maken om dit filter te testen:

openbare klasse JerseyClient {private static String URI_GREETINGS = "// localhost: 8080 / jersey / greetings"; openbare statische String getHelloGreeting () {return createClient (). target (URI_GREETINGS) .request () .get (String.class); } privé statische Client createClient () {ClientConfig config = nieuwe ClientConfig (); config.register (RequestClientFilter.class); retourneer ClientBuilder.newClient (config); }}

Merk op dat we het filter aan de clientconfiguratie moeten toevoegen om het te registreren.

Ten slotte maken we ook een filter voor het antwoord in de client.

Dit werkt op een vergelijkbare manier als die in de server, maar implementeert het ClientResponseFilter koppel:

@Provider public class ResponseClientFilter implementeert ClientResponseFilter {@Override public void filter (ClientRequestContext requestContext, ClientResponseContext responseContext) gooit IOException {responseContext.getHeaders () .add ("X-Test-Client", "Test response client filter"); }}

Nogmaals, de ClientRequestContext is voor alleen-lezen doeleinden.

4. Onderscheppers

Interceptors zijn meer verbonden met het rangschikken en niet-markeren van de HTTP-berichtlichamen die zijn vervat in de verzoeken en de antwoorden. Ze kunnen zowel op de server als op de client worden gebruikt.

Houd daar rekening mee ze worden uitgevoerd na de filters en alleen als er een berichttekst aanwezig is.

Er zijn twee soorten onderscheppers: ReaderInterceptor en WriterInterceptor, en ze zijn hetzelfde voor zowel de server als de client.

Vervolgens gaan we een andere bron op onze server maken - die toegankelijk is via een POST en die een parameter in de body ontvangt, zodat interceptors worden uitgevoerd bij het openen ervan:

@POST @Path ("/ custom") openbare reactie getCustomGreeting (stringnaam) {return Response.status (Status.OK.getStatusCode ()) .build (); }

We zullen ook een nieuwe methode toevoegen aan onze Jersey-client - om deze nieuwe bron te testen:

openbare statische reactie getCustomGreeting () {return createClient (). target (URI_GREETINGS + "/ custom") .request () .post (Entity.text ("custom")); }

4.1. Implementeren van een ReaderInterceptor

Met lezer-interceptors kunnen we inkomende streams manipuleren, zodat we ze kunnen gebruiken om het verzoek aan de serverzijde of het antwoord aan de clientzijde te wijzigen.

Laten we een interceptor aan de serverzijde maken om een ​​aangepast bericht te schrijven in de hoofdtekst van het onderschepte verzoek:

@Provider openbare klasse RequestServerReaderInterceptor implementeert ReaderInterceptor {@Override openbaar object aroundReadFrom (ReaderInterceptorContext-context) gooit IOException, WebApplicationException {InputStream is = context.getInputStream (); String body = nieuwe BufferedReader (nieuwe InputStreamReader (is)). Lines () .collect (Collectors.joining ("\ n")); context.setInputStream (nieuwe ByteArrayInputStream ((body + "bericht toegevoegd in interceptor serverlezer"). getBytes ())); terugkeer context.proceed (); }}

Let erop dat we moeten de doorgaan() methodeom de volgende interceptor in de keten te bellen. Zodra alle onderscheppers zijn uitgevoerd, wordt de juiste lezer van de berichttekst gebeld.

3.2. Implementeren van een WriterInterceptor

Writer-interceptors werken op een vergelijkbare manier als reader-interceptors, maar ze manipuleren de uitgaande streams - zodat we ze kunnen gebruiken met het verzoek aan de clientkant of met het antwoord aan de serverkant.

Laten we een schrijver-interceptor maken om een ​​bericht aan het verzoek toe te voegen, aan de clientzijde:

@Provider public class RequestClientWriterInterceptor implementeert WriterInterceptor {@Override public void aroundWriteTo (WriterInterceptorContext context) gooit IOException, WebApplicationException {context.getOutputStream () .write (("Bericht toegevoegd in de schrijver interceptor aan de clientzijde"). GetBytes; context.proceed (); }}

Nogmaals, we moeten de methode aanroepen doorgaan() om de volgende interceptor te bellen.

Wanneer alle onderscheppers zijn uitgevoerd, wordt de juiste schrijver van de berichttekst gebeld.

Vergeet niet dat u deze interceptor moet registreren in de clientconfiguratie, zoals we eerder deden met het clientfilter:

privé statische Client createClient () {ClientConfig config = nieuwe ClientConfig (); config.register (RequestClientFilter.class); config.register (RequestWriterInterceptor.class); retourneer ClientBuilder.newClient (config); }

5. Uitvoeringsbevel

Laten we alles wat we tot nu toe hebben gezien samenvatten in een diagram dat laat zien wanneer de filters en interceptors worden uitgevoerd tijdens een verzoek van een client naar een server:

Zoals we kunnen zien, de filters worden altijd eerst uitgevoerd en de onderscheppers worden uitgevoerd vlak voordat de juiste lezer of schrijver van de berichttekst wordt gebeld.

Als we de filters en interceptors bekijken die we hebben gemaakt, worden ze in de volgende volgorde uitgevoerd:

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

Bovendien, als we meerdere filters of interceptors hebben, kunnen we de exacte uitvoeringsvolgorde specificeren door ze te annoteren met de @Prioriteit annotatie.

De prioriteit wordt gespecificeerd met een Geheel getal en sorteert de filters en onderscheppers in oplopende volgorde voor de verzoeken en in aflopende volgorde voor de antwoorden.

Laten we een prioriteit toevoegen aan onze RestrictedOperationsRequestFilter:

@Provider @Priority (Priorities.AUTHORIZATION) openbare klasse RestrictedOperationsRequestFilter implementeert ContainerRequestFilter {// ...}

Merk op dat we een vooraf gedefinieerde prioriteit hebben gebruikt voor autorisatiedoeleinden.

6. Naamsbinding

De filters en interceptors die we tot nu toe hebben gezien, worden globaal genoemd omdat ze worden uitgevoerd voor elk verzoek en elke reactie.

Echter, ze kunnen ook worden gedefinieerd om alleen te worden uitgevoerd voor specifieke resourcemethoden, wat naambinding wordt genoemd.

6.1. Statische binding

Een manier om de naam te binden is statisch door een bepaalde annotatie te maken die zal worden gebruikt in de gewenste bron. Deze annotatie moet de @NaamBinding meta-annotatie.

Laten we er een maken in onze applicatie:

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

Daarna kunnen we hiermee enkele bronnen annoteren @BuienRadarNL annotatie:

@GET @HelloBinding openbare String getHelloGreeting () {retourneer "hallo"; }

Ten slotte gaan we ook een van onze filters annoteren met deze annotatie, dus dit filter wordt alleen uitgevoerd voor verzoeken en antwoorden die toegang hebben tot de getHelloGreeting () methode:

@Provider @Priority (Priorities.AUTHORIZATION) @HelloBinding openbare klasse RestrictedOperationsRequestFilter implementeert ContainerRequestFilter {// ...}

Houd er rekening mee dat onze RestrictedOperationsRequestFilter wordt niet meer geactiveerd voor de rest van de bronnen.

6.2. Dynamische binding

Een andere manier om dit te doen is door een dynamische binding te gebruiken, die tijdens het opstarten in de configuratie wordt geladen.

Laten we eerst nog een bron aan onze server toevoegen voor deze sectie:

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

Laten we nu een binding voor deze resource maken door het DynamicFeature koppel:

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

In dit geval associëren we de getHiGreeting () methode naar de ResponseServerFilter die we eerder hadden gemaakt.

Het is belangrijk om te onthouden dat we het @Provider annotatie van dit filter, aangezien we het nu configureren via DynamicFeature.

Als we dit niet doen, wordt het filter twee keer uitgevoerd: een keer als een globaal filter en een andere keer als een filter gebonden aan de getHiGreeting () methode.

7. Conclusie

In deze tutorial hebben we ons gericht op het begrijpen van hoe filters en interceptors werken in Jersey 2 en hoe we ze in een webapplicatie kunnen gebruiken.

Zoals altijd is de volledige broncode voor de voorbeelden beschikbaar op GitHub.


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