Læsning af HttpServletRequest flere gange om foråret

1. Introduktion

I denne vejledning lærer vi, hvordan man læser kroppen fra HttpServletRequest flere gange ved hjælp af Spring.

HttpServletRequest er en grænseflade, der afslører getInputStream () metode til at læse kroppen. Som standard, dataene fra dette InputStream kan kun læses en gang.

2. Maven-afhængigheder

Den første ting, vi har brug for, er det rette fjeder-webmvc og javax.servlet afhængigheder:

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

Også, da vi bruger ansøgning / json indholdstype, den jackson-databind afhængighed er påkrævet:

 com.fasterxml.jackson.core jackson-databind 2.10.0 

Spring bruger dette bibliotek til at konvertere til og fra JSON.

3. Forår ContentCachingRequestWrapper

Foråret giver en ContentCachingRequestWrapper klasse. Denne klasse giver en metode, getContentAsByteArray () at læse kroppen flere gange.

Denne klasse har dog en begrænsning: Vi kan ikke læse kroppen flere gange ved hjælp af getInputStream () og getReader () metoder.

Denne klasse cacher anmodningsorganet ved at forbruge InputStream. Hvis vi læser InputStream i et af filtrene kan andre efterfølgende filtre i filterkæden ikke læse det længere. På grund af denne begrænsning er denne klasse ikke egnet i alle situationer.

For at overvinde denne begrænsning, lad os nu se på en mere generel løsning.

4. Udvidelse HttpServletRequest

Lad os oprette en ny klasse - CachedBodyHttpServletRequest - som strækker sig HttpServletRequestWrapper. På denne måde behøver vi ikke tilsidesætte alle de abstrakte metoder til HttpServletRequest interface.

HttpServletRequestWrapper klasse har to abstrakte metoder getInputStream () og getReader (). Vi tilsidesætter begge disse metoder og opretter en ny konstruktør.

4.1. Konstruktøren

Lad os først oprette en konstruktør. Inde i det læser vi kroppen fra selve InputStream og opbevar den i en byte [] objekt:

offentlig klasse CachedBodyHttpServletRequest udvider HttpServletRequestWrapper {privat byte [] cachedBody; offentlig CachedBodyHttpServletRequest (HttpServletRequest anmodning) kaster IOException {super (anmodning); InputStream-anmodningInputStream = anmodning.getInputStream (); this.cachedBody = StreamUtils.copyToByteArray (requestInputStream); }}

Som et resultat vil vi være i stand til at læse kroppen flere gange.

4.2. getInputStream ()

Lad os derefter tilsidesætte getInputStream () metode. Vi bruger denne metode til at læse den rå krop og konvertere den til et objekt.

I denne metode vil vi oprette og returnere et nyt objekt af CachedBodyServletInputStream klasse (en implementering af ServletInputStream):

@ Override offentlige ServletInputStream getInputStream () kaster IOException {returner nye CachedBodyServletInputStream (this.cachedBody); }

4.3. getReader ()

Så tilsidesætter vi getReader () metode. Denne metode returnerer a BufferedReader objekt:

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

5. Implementering af ServletInputStream

Lad os oprette en klasse - CachedBodyServletInputStream - som vil implementere ServletInputStream. I denne klasse opretter vi en ny konstruktør samt tilsidesætter er færdig(), er klar() og Læs() metoder.

5.1. Konstruktøren

Lad os først oprette en ny konstruktør, der tager et byte-array.

Inde i det opretter vi en ny ByteArrayInputStream eksempel ved hjælp af denne byte-array. Derefter tildeler vi den til den globale variabel cachedBodyInputStream:

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

5.2. Læs()

Derefter tilsidesætter vi Læs() metode. I denne metode vil vi ringe ByteArrayInputStream # læs:

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

5.3. er færdig()

Så tilsidesætter vi er færdig() metode. Denne metode angiver, om InputStream har flere data at læse eller ej. Det vender tilbage rigtigt når nul byte er tilgængelig til at læse:

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

5.4. er klar()

På samme måde tilsidesætter vi er klar() metode. Denne metode angiver, om InputStream er klar til læsning eller ej.

Da vi allerede har kopieret InputStream i et byte-array vender vi tilbage rigtigt for at angive, at den altid er tilgængelig:

@Override public boolean isReady () {return true; }

6. Filteret

Lad os endelig oprette et nyt filter for at gøre brug af CachedBodyHttpServletRequest klasse. Her udvider vi forårets OncePerRequestFilter klasse. Denne klasse har en abstrakt metode doFilterInternal ().

I denne metode vil vi oprette et objekt af CachedBodyHttpServletRequest klasse fra det egentlige anmodningsobjekt:

CachedBodyHttpServletRequest cachedBodyHttpServletRequest = ny CachedBodyHttpServletRequest (anmodning);

Så vil vi videregiv dette nye anmodningsindpakningsobjekt til filterkæden. Så alle efterfølgende opkald til getInputStream() -metoden påberåber den tilsidesatte metode:

filterChain.doFilter (cachedContentHttpServletRequest, respons);

7. Konklusion

I denne vejledning gik vi hurtigt gennem ContentCachingRequestWrapper klasse. Vi så også dens begrænsninger.

Derefter oprettede vi en ny implementering af HttpServletRequestWrapper klasse. Vi overstyrede getInputStream () metode til at returnere et objekt fra ServletInputStream klasse.

Endelig oprettede vi et nyt filter til at sende anmodningsindpakningsobjektet til filterkæden. Så vi var i stand til at læse anmodningen flere gange.

Den fulde kildekode for eksemplerne kan findes på GitHub.