From 643f99281f487ce7e83fa00312da163292b5e019 Mon Sep 17 00:00:00 2001 From: Forb Yuan Date: Mon, 12 Jun 2023 11:49:15 +0800 Subject: [PATCH] BAEL-4860: Java IllegalStateException: "getInputStream() has already been called for this request" (#14121) * [BAEL-6203] Deserialize Generic Type with Jackson * BAEL-4860: Java IllegalStateException: "getInputStream() has already been called for this request" * BAEL-4860: Move code to new module spring-web-module/spring-mvc-java-3. * BAEL-4860: update pom * BAEL-4860: make the test methods public --- spring-web-modules/pom.xml | 1 + .../spring-mvc-java-3/README.md | 1 + spring-web-modules/spring-mvc-java-3/pom.xml | 40 ++++++++++++++++ .../filters/CacheRequestContentFilter.java | 23 +++++++++ .../baeldung/filters/HttpRequestUnitTest.java | 47 +++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 spring-web-modules/spring-mvc-java-3/README.md create mode 100644 spring-web-modules/spring-mvc-java-3/pom.xml create mode 100644 spring-web-modules/spring-mvc-java-3/src/main/java/com/baeldung/filters/CacheRequestContentFilter.java create mode 100644 spring-web-modules/spring-mvc-java-3/src/test/java/com/baeldung/filters/HttpRequestUnitTest.java diff --git a/spring-web-modules/pom.xml b/spring-web-modules/pom.xml index a9970e85c0..d513822ea3 100644 --- a/spring-web-modules/pom.xml +++ b/spring-web-modules/pom.xml @@ -28,6 +28,7 @@ spring-mvc-forms-thymeleaf spring-mvc-java spring-mvc-java-2 + spring-mvc-java-3 spring-mvc-velocity spring-mvc-views spring-mvc-webflow diff --git a/spring-web-modules/spring-mvc-java-3/README.md b/spring-web-modules/spring-mvc-java-3/README.md new file mode 100644 index 0000000000..7d843af9ea --- /dev/null +++ b/spring-web-modules/spring-mvc-java-3/README.md @@ -0,0 +1 @@ +### Relevant Articles: diff --git a/spring-web-modules/spring-mvc-java-3/pom.xml b/spring-web-modules/spring-mvc-java-3/pom.xml new file mode 100644 index 0000000000..3a49a5f9af --- /dev/null +++ b/spring-web-modules/spring-mvc-java-3/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + spring-mvc-java-3 + 0.1-SNAPSHOT + spring-mvc-java-3 + war + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + javax.servlet + javax.servlet-api + + + org.springframework + spring-webmvc + + + + + spring-mvc-java-3 + + + src/main/resources + true + + + + + \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-java-3/src/main/java/com/baeldung/filters/CacheRequestContentFilter.java b/spring-web-modules/spring-mvc-java-3/src/main/java/com/baeldung/filters/CacheRequestContentFilter.java new file mode 100644 index 0000000000..907ec24700 --- /dev/null +++ b/spring-web-modules/spring-mvc-java-3/src/main/java/com/baeldung/filters/CacheRequestContentFilter.java @@ -0,0 +1,23 @@ +package com.baeldung.filters; + +import org.springframework.web.util.ContentCachingRequestWrapper; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +@WebFilter(urlPatterns = "/*") +public class CacheRequestContentFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { + if (request instanceof HttpServletRequest) { + String contentType = request.getContentType(); + if (contentType == null || !contentType.contains("multipart/form-data")) { + request = new ContentCachingRequestWrapper((HttpServletRequest) request); + } + } + chain.doFilter(request, response); + } +} diff --git a/spring-web-modules/spring-mvc-java-3/src/test/java/com/baeldung/filters/HttpRequestUnitTest.java b/spring-web-modules/spring-mvc-java-3/src/test/java/com/baeldung/filters/HttpRequestUnitTest.java new file mode 100644 index 0000000000..cd369d9118 --- /dev/null +++ b/spring-web-modules/spring-mvc-java-3/src/test/java/com/baeldung/filters/HttpRequestUnitTest.java @@ -0,0 +1,47 @@ +package com.baeldung.filters; + +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.util.ContentCachingRequestWrapper; + +import javax.servlet.Filter; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +public class HttpRequestUnitTest { + + @Test + public void givenHttpServletRequest_whenCalling_getReaderAfter_getInputStream_thenThrowIllegalStateException() throws IOException { + HttpServletRequest request = new MockHttpServletRequest(); + try (ServletInputStream ignored = request.getInputStream()) { + IllegalStateException exception = assertThrows(IllegalStateException.class, request::getReader); + assertEquals("Cannot call getReader() after getInputStream() has already been called for the current request", + exception.getMessage()); + } + } + + @Test + public void givenServletRequest_whenDoFilter_thenCanCallBoth() throws ServletException, IOException { + MockHttpServletRequest req = new MockHttpServletRequest(); + MockHttpServletResponse res = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + + Filter filter = new CacheRequestContentFilter(); + filter.doFilter(req, res, chain); + + ServletRequest request = chain.getRequest(); + assertTrue(request instanceof ContentCachingRequestWrapper); + + // now we can call both getInputStream() and getReader() + request.getInputStream(); + request.getReader(); + } + +}