HttpServletRequest im Frühjahr mehrmals lesen

1. Einleitung

In diesem Tutorial lernen wir, wie Sie den Body aus dem HttpServletRequest mehrmals mit Spring lesen .

HttpServletRequest ist eine Schnittstelle, die die Methode getInputStream () zum Lesen des Körpers verfügbar macht . Standardmäßig können die Daten aus diesem InputStream nur einmal gelesen werden .

2. Maven-Abhängigkeiten

Das erste , was wir brauchen, ist die entsprechende feder webmvc und javax.servlet Abhängigkeiten:

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

Da wir den Inhaltstyp application / json verwenden, ist außerdem die Abhängigkeit von jackson-databaseind erforderlich:

 com.fasterxml.jackson.core jackson-databind 2.10.0 

Spring verwendet diese Bibliothek zum Konvertieren von und nach JSON.

3. Spring ContentCachingRequestWrapper

Spring bietet eine ContentCachingRequestWrapper- Klasse. Diese Klasse bietet die Methode getContentAsByteArray () zum mehrmaligen Lesen des Körpers .

Diese Klasse hat jedoch eine Einschränkung: Mit den Methoden getInputStream () und getReader () können wir den Textkörper nicht mehrmals lesen .

Diese Klasse speichert den Anforderungshauptteil zwischen, indem sie den InputStream verwendet . Wenn wir den InputStream in einem der Filter lesen , können andere nachfolgende Filter in der Filterkette ihn nicht mehr lesen. Aufgrund dieser Einschränkung ist diese Klasse nicht in allen Situationen geeignet.

Um diese Einschränkung zu überwinden, schauen wir uns nun eine allgemeinere Lösung an.

4. Erweitern von HttpServletRequest

Erstellen wir eine neue Klasse - CachedBodyHttpServletRequest -, die HttpServletRequestWrapper erweitert . Auf diese Weise müssen nicht alle abstrakten Methoden der HttpServletRequest- Schnittstelle überschrieben werden .

Die HttpServletRequestWrapper- Klasse verfügt über zwei abstrakte Methoden, getInputStream () und getReader () . Wir werden beide Methoden überschreiben und einen neuen Konstruktor erstellen.

4.1. Der Konstruktor

Zuerst erstellen wir einen Konstruktor. Darin lesen wir den Body aus dem eigentlichen InputStream und speichern ihn in einem Byte [] -Objekt:

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private byte[] cachedBody; public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); InputStream requestInputStream = request.getInputStream(); this.cachedBody = StreamUtils.copyToByteArray(requestInputStream); } }

Infolgedessen können wir den Körper mehrmals lesen.

4.2. getInputStream ()

Als nächstes überschreiben wir die Methode getInputStream () . Wir werden diese Methode verwenden, um den Rohkörper zu lesen und ihn in ein Objekt umzuwandeln.

In dieser Methode erstellen wir ein neues Objekt der CachedBodyServletInputStream- Klasse (eine Implementierung von ServletInputStream) und geben es zurück :

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

4.3. getReader ()

Dann überschreiben wir die Methode getReader () . Diese Methode gibt ein BufferedReader- Objekt zurück:

@Override public BufferedReader getReader() throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody); return new BufferedReader(new InputStreamReader(byteArrayInputStream)); }

5. Implementierung von ServletInputStream

Erstellen wir eine Klasse - CachedBodyServletInputStream - die ServletInputStream implementiert . In dieser Klasse erstellen wir einen neuen Konstruktor und überschreiben die Methoden isFinished () , isReady () und read () .

5.1. Der Konstruktor

Zuerst erstellen wir einen neuen Konstruktor, der ein Byte-Array verwendet.

Darin erstellen wir eine neue ByteArrayInputStream- Instanz mit diesem Byte-Array. Danach weisen wir es der globalen Variablen cachedBodyInputStream zu:

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

5.2. lesen()

Dann überschreiben wir die read () -Methode . In dieser Methode rufen wir ByteArrayInputStream # read auf:

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

5.3. ist fertig()

Dann überschreiben wir die isFinished () -Methode. Diese Methode gibt an, ob InputStream mehr Daten zum Lesen hat oder nicht. Es gibt true zurück , wenn null Bytes zum Lesen verfügbar sind:

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

5.4. ist bereit()

In ähnlicher Weise überschreiben wir die isReady () -Methode. Diese Methode gibt an, ob InputStream zum Lesen bereit ist oder nicht.

Da wir InputStream bereits in ein Byte-Array kopiert haben , geben wir true zurück, um anzuzeigen, dass es immer verfügbar ist:

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

6. The Filter

Finally, let's create a new filter to make use of the CachedBodyHttpServletRequest class. Here we'll extend Spring's OncePerRequestFilter class. This class has an abstract method doFilterInternal().

In this method, we'll create an object of the CachedBodyHttpServletRequest class from the actual request object:

CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(request);

Then we'll pass this new request wrapper object to the filter chain. So, all the subsequent calls to the getInputStream() method will invoke the overridden method:

filterChain.doFilter(cachedContentHttpServletRequest, response);

7. Conclusion

In this tutorial, we quickly walked through the ContentCachingRequestWrapper class. We also saw its limitations.

Anschließend haben wir eine neue Implementierung der HttpServletRequestWrapper- Klasse erstellt. Wir haben die Methode getInputStream () überschrieben , um ein Objekt der ServletInputStream- Klasse zurückzugeben.

Schließlich haben wir einen neuen Filter erstellt, um das Request-Wrapper-Objekt an die Filterkette zu übergeben. So konnten wir die Anfrage mehrmals lesen.

Den vollständigen Quellcode der Beispiele finden Sie auf GitHub.