Les HttpServletRequest flere ganger om våren

1. Introduksjon

I denne opplæringen lærer vi hvordan du leser kroppen fra HttpServletRequest flere ganger ved hjelp av våren.

HttpServletRequest er et grensesnitt som avslører getInputStream () metode for å lese kroppen. Som standard, dataene fra dette InputStream kan bare leses en gang.

2. Maven-avhengigheter

Det første vi trenger er riktig vår-webmvc og javax.servlet avhengigheter:

 org.springframework spring-webmvc 5.2.0.RELEASE javax.servlet javax.servlet-api 4.0.1 

Siden vi bruker søknad / json innholdstype, den jackson-databind avhengighet kreves:

 com.fasterxml.jackson.core jackson-databind 2.10.0 

Spring bruker dette biblioteket til å konvertere til og fra JSON.

3. Vårens ContentCachingRequestWrapper

Våren gir en ContentCachingRequestWrapper klasse. Denne klassen gir en metode, getContentAsByteArray () å lese kroppen flere ganger.

Denne klassen har imidlertid en begrensning: Vi kan ikke lese kroppen flere ganger ved hjelp av getInputStream () og getReader () metoder.

Denne klassen cacher forespørselsorganet ved å konsumere InputStream. Hvis vi leser InputStream i et av filtrene, så kan andre påfølgende filtre i filterkjeden ikke lese det lenger. På grunn av denne begrensningen er denne klassen ikke egnet i alle situasjoner.

For å overvinne denne begrensningen, la oss nå ta en titt på en mer generell løsning.

4. Utvide HttpServletRequest

La oss lage en ny klasse - CachedBodyHttpServletRequest - som strekker seg HttpServletRequestWrapper. På denne måten trenger vi ikke å overstyre alle de abstrakte metodene til HttpServletRequest grensesnitt.

HttpServletRequestWrapper klassen har to abstrakte metoder getInputStream () og getReader (). Vi overstyrer begge disse metodene og oppretter en ny konstruktør.

4.1. Konstruktøren

Først, la oss lage en konstruktør. Inne i den vil vi lese kroppen fra selve InputStream og oppbevar den i en byte [] gjenstand:

offentlig klasse CachedBodyHttpServletRequest utvider HttpServletRequestWrapper {privat byte [] cachedBody; offentlig CachedBodyHttpServletRequest (HttpServletRequest forespørsel) kaster IOException {super (forespørsel); InputStream requestInputStream = request.getInputStream (); this.cachedBody = StreamUtils.copyToByteArray (requestInputStream); }}

Som et resultat vil vi kunne lese kroppen flere ganger.

4.2. getInputStream ()

La oss deretter overstyre getInputStream () metode. Vi bruker denne metoden til å lese den rå kroppen og konvertere den til et objekt.

I denne metoden vil vi lage og returnere et nytt objekt av CachedBodyServletInputStream klasse (en implementering av ServletInputStream):

@Override public ServletInputStream getInputStream () kaster IOException {return new CachedBodyServletInputStream (this.cachedBody); }

4.3. getReader ()

Så overstyrer vi getReader () metode. Denne metoden returnerer a BufferedReader gjenstand:

@Override public BufferedReader getReader () kaster IOException {ByteArrayInputStream byteArrayInputStream = ny ByteArrayInputStream (this.cachedBody); returner ny BufferedReader (ny InputStreamReader (byteArrayInputStream)); }

5. Implementering av ServletInputStream

La oss lage en klasse - CachedBodyServletInputStream - som vil implementere ServletInputStream. I denne klassen oppretter vi en ny konstruktør og overstyrer er ferdig(), er klar() og lese() metoder.

5.1. Konstruktøren

La oss først lage en ny konstruktør som tar et byte-utvalg.

Inne i den lager vi en ny ByteArrayInputStream forekomst ved hjelp av den byte-matrisen. Etter det tilordner vi den til den globale variabelen cachedBodyInputStream:

offentlig klasse CachedBodyServletInputStream utvider ServletInputStream {private InputStream cachedBodyInputStream; public CachedBodyServletInputStream (byte [] cachedBody) {this.cachedBodyInputStream = new ByteArrayInputStream (cachedBody); }}

5.2. lese()

Så overstyrer vi lese() metode. I denne metoden vil vi ringe ByteArrayInputStream # les:

@Override public int read () kaster IOException {return cachedBodyInputStream.read (); }

5.3. er ferdig()

Så overstyrer vi er ferdig() metode. Denne metoden indikerer om InputStream har mer data å lese eller ikke. Det kommer tilbake ekte når null byte er tilgjengelige å lese:

@Override public boolean isFinished () {return cachedBody.available () == 0; }

5.4. er klar()

På samme måte overstyrer vi er klar() metode. Denne metoden indikerer om InputStream er klar for lesing eller ikke.

Siden vi allerede har kopiert InputStream i et byte-utvalg, kommer vi tilbake ekte for å indikere at den alltid er tilgjengelig:

@Override offentlig boolsk isReady () {return true; }

6. Filteret

Til slutt, la oss lage et nytt filter for å bruke CachedBodyHttpServletRequest klasse. Her vil vi utvide vårens OncePerRequestFilter klasse. Denne klassen har en abstrakt metode doFilterInternal ().

I denne metoden vil vi lage et objekt av CachedBodyHttpServletRequest klasse fra selve forespørselsobjektet:

CachedBodyHttpServletRequest cachedBodyHttpServletRequest = ny CachedBodyHttpServletRequest (forespørsel);

Så vil vi send dette nye forespørselen til filterkjeden. Så alle påfølgende samtaler til getInputStream() -metoden vil påkalle den overstyrte metoden:

filterChain.doFilter (cachedContentHttpServletRequest, response);

7. Konklusjon

I denne opplæringen gikk vi raskt gjennom ContentCachingRequestWrapper klasse. Vi så også begrensningene.

Deretter opprettet vi en ny implementering av HttpServletRequestWrapper klasse. Vi overstyrte getInputStream () metode for å returnere et objekt av ServletInputStream klasse.

Til slutt opprettet vi et nytt filter for å sende forespørselen til objektet til filterkjeden. Så vi var i stand til å lese forespørselen flere ganger.

Den fulle kildekoden til eksemplene finner du på GitHub.