HttpServletRequest meerdere keren lezen in het voorjaar

1. Inleiding

In deze tutorial leren we hoe we de body kunnen lezen uit de HttpServletRequest meerdere keren met Spring.

HttpServletRequest is een interface die blootstelt getInputStream () methode om het lichaam te lezen. Standaard, de gegevens hiervan InputStream kan slechts één keer worden gelezen.

2. Maven afhankelijkheden

Het eerste dat we nodig hebben, is het juiste lente-webmvc en javax.servlet afhankelijkheden:

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

Omdat we de applicatie / json content-type, het jackson-databind afhankelijkheid is vereist:

 com.fasterxml.jackson.core jackson-databind 2.10.0 

Spring gebruikt deze bibliotheek om van en naar JSON te converteren.

3. Lente ContentCachingRequestWrapper

De lente biedt een ContentCachingRequestWrapper klasse. Deze klasse biedt een methode, getContentAsByteArray () om het lichaam meerdere keren te lezen.

Deze klasse heeft echter een beperking: We kunnen de body niet meerdere keren lezen met de getInputStream () en getReader () methoden.

Deze klasse slaat de hoofdtekst van het verzoek op door de InputStream. Als we het InputStream in een van de filters, dan kunnen andere volgende filters in de filterketen het niet meer lezen. Vanwege deze beperking is deze klasse niet in alle situaties geschikt.

Laten we, om deze beperking te overwinnen, nu eens kijken naar een meer algemene oplossing.

4. Verlengen HttpServletRequest

Laten we een nieuwe klas - CachedBodyHttpServletRequest - die zich uitstrekt HttpServletRequestWrapper. Op deze manier hoeven we niet alle abstracte methoden van de HttpServletRequest koppel.

HttpServletRequestWrapper klasse heeft twee abstracte methoden getInputStream () en getReader (). We overschrijven beide methoden en maken een nieuwe constructor.

4.1. De constructeur

Laten we eerst een constructor maken. Binnenin lezen we het lichaam van de feitelijke InputStream en bewaar het in een byte[] voorwerp:

openbare klasse CachedBodyHttpServletRequest breidt HttpServletRequestWrapper {privé byte [] cachedBody uit; openbare CachedBodyHttpServletRequest (verzoek HttpServletRequest) gooit IOException {super (verzoek); InputStream requestInputStream = request.getInputStream (); this.cachedBody = StreamUtils.copyToByteArray (requestInputStream); }}

Als gevolg hiervan kunnen we de body meerdere keren lezen.

4.2. getInputStream ()

Laten we vervolgens het getInputStream () methode. We gebruiken deze methode om de onbewerkte body te lezen en om te zetten in een object.

Bij deze methode zullen we maak en retourneer een nieuw object van CachedBodyServletInputStream klasse (een implementatie van ServletInputStream):

@Override openbare ServletInputStream getInputStream () gooit IOException {retourneer nieuwe CachedBodyServletInputStream (this.cachedBody); }

4.3. getReader ()

Vervolgens overschrijven we de getReader () methode. Deze methode retourneert een BufferedReader voorwerp:

@Override openbare BufferedReader getReader () gooit IOException {ByteArrayInputStream byteArrayInputStream = nieuwe ByteArrayInputStream (this.cachedBody); retourneer nieuwe BufferedReader (nieuwe InputStreamReader (byteArrayInputStream)); }

5. Implementatie van ServletInputStream

Laten we een klas - CachedBodyServletInputStream - die zal implementeren ServletInputStream. In deze klasse zullen we een nieuwe constructor maken en de is klaar(), is klaar() en lezen() methoden.

5.1. De constructeur

Laten we eerst een nieuwe constructor maken die een byte-array nodig heeft.

Daarbinnen maken we een nieuw ByteArrayInputStream instantie met behulp van die byte-array. Daarna wijzen we het toe aan de globale variabele cachedBodyInputStream:

openbare klasse CachedBodyServletInputStream breidt ServletInputStream uit {privé InputStream cachedBodyInputStream; openbare CachedBodyServletInputStream (byte [] cachedBody) {this.cachedBodyInputStream = nieuwe ByteArrayInputStream (cachedBody); }}

5.2. lezen()

Vervolgens overschrijven we de lezen() methode. Bij deze methode bellen we ByteArrayInputStream # lezen:

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

5.3. is klaar()

Vervolgens overschrijven we de is klaar() methode. Deze methode geeft aan of InputStream heeft meer gegevens om te lezen of niet. Het keert terug waar wanneer nul bytes beschikbaar zijn om te lezen:

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

5.4. is klaar()

Evenzo zullen we de is klaar() methode. Deze methode geeft aan of InputStream is klaar om te lezen of niet.

Omdat we al hebben gekopieerd InputStream in een byte-array zullen we terugkeren waar om aan te geven dat het altijd beschikbaar is:

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

6. Het filter

Laten we tot slot een nieuw filter maken om gebruik te maken van het CachedBodyHttpServletRequest klasse. Hier verlengen we de lente OncePerRequestFilter klasse. Deze klasse heeft een abstracte methode doFilterInternal ().

Bij deze methode zullen we maak een object van de CachedBodyHttpServletRequest class van het daadwerkelijke verzoekobject:

CachedBodyHttpServletRequest cachedBodyHttpServletRequest = nieuwe CachedBodyHttpServletRequest (verzoek);

Dan zullen we geef dit nieuwe verzoek-wrapper-object door aan de filterketen. Dus alle volgende oproepen naar de getInputStream() methode zal de overschreven methode aanroepen:

filterChain.doFilter (cachedContentHttpServletRequest, antwoord);

7. Conclusie

In deze tutorial liepen we snel door het ContentCachingRequestWrapper klasse. We zagen ook de beperkingen ervan.

Vervolgens hebben we een nieuwe implementatie gemaakt van het HttpServletRequestWrapper klasse. We hebben de getInputStream () methode om een ​​object van te retourneren ServletInputStream klasse.

Ten slotte hebben we een nieuw filter gemaakt om het request wrapper-object door te geven aan de filterketen. We konden het verzoek dus meerdere keren lezen.

De volledige broncode van de voorbeelden is te vinden op GitHub.