Not getting response body when using ContentCachingResponseWrapper
Asked Answered
E

3

7

I have a Spring Boot API in Scala with multiple endpoints. All endpoints are async and return DeferredResult. I want to use filters to log response body in some cases. I have created a filter with order 1 to cache requests and response as below:

@Component
@Order(1)
class ContentCachingFilter extends OncePerRequestFilter {
  override def doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain): Unit = {
    val requestToCache  = new ContentCachingRequestWrapper(request)
    val responseToCache = new ContentCachingResponseWrapper(response)
    filterChain.doFilter(requestToCache, responseToCache)
    responseToCache.copyBodyToResponse()
  }
}

My logging filter is similar to below (removed domain specific logic):

@Component
@Order(2)
class RequestResponseLoggingFilter extends OncePerRequestFilter {

  override def doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain): Unit = {

    logger.info(
      s"Request -> URL : ${request.getMethod} ${request.getRequestURI} ${request.getParameterMap.asScala.stringRepresentation()}")
    logger.debug(
      s"Request Headers -> Content-type : ${request.getContentType} \\n Headers : ${new ServletServerHttpRequest(request).getHeaders}")

    filterChain.doFilter(request, response)

    logger.info(s"Response -> Status : ${response.getStatus}")

  }
}

I have to use ContentCachingResponseWrapper because I want to log the response body. However, even without trying to log the response body and just by introducing ContentCachingResponseWrapper, I don't get any response for calls to any endpoint. A local running instance returns an output like this on curl:

HTTP/1.1 200
Content-Type: application/json
Content-Length: 197
.
.
.
<
* transfer closed with 197 bytes remaining to read
* stopped the pause stream!
* Closing connection 0
curl: (18) transfer closed with 197 bytes remaining to read

If I remove the ContentCachingFilter, the logging filter above works fine but if I try to log response body, the response stream is consumed and the response is no longer generated. Hence I need to use ContentCachingFilter.

I did find out that all this works if the endpoints are synchronous. Could someone help me find out what is the problem and how to make this work with asynchronous endpoint?

Eburnation answered 21/4, 2020 at 12:45 Comment(0)
H
14
responseToCache.copyBodyToResponse()

change to :

if (requestToCache.isAsyncStarted()) {
  requestToCache.getAsyncContext().addListener(new AsyncListener() {
      public void onComplete(AsyncEvent asyncEvent) throws IOException {
          responseToCache.copyBodyToResponse()
      }

      public void onTimeout(AsyncEvent asyncEvent) throws IOException {
      }

      public void onError(AsyncEvent asyncEvent) throws IOException {
      }

      public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
      }
  })
} else {
  responseToCache.copyBodyToResponse()
}

Hendiadys answered 13/12, 2021 at 11:58 Comment(4)
This worked in my case.Pitching
Spent all day looking for this answer. Kept seeing a 200 status code in the wrapped response in code, yet a 500 in the actual HTTP response. This async check was the key and now I can log all status codes. Thank you!Heredity
You saved my day, thank you! ( In my case, the execution order: sync-filterChain(in)->Controller of DeferredResult<VO> method -> sync-filterChain(out) -> async-filterChain(in) ->async-filterChain(out). It seems that the response returns to the front-end just after sync-filterChain(out), not after async-filterChain(out), so of cause the sync-response-body is empty! )Backbreaking
Spent so much time for the weird empty response issue. Thank you so muchBainmarie
E
0
val requestToCache = new ContentCachingRequestWrapper(request);
val responseToCache = new ContentCachingResponseWrapper(response);

This two line change to:

  if(!(request instanceof ContentCachingRequestWrapper)) {
     requestToCache = new ContentCachingRequestWrapper(request);
  }
  if(!(request instanceof ContentCachingResponseWrapper)){
     responseToCache = new ContentCachingResponseWrapper(request);
  }
Evaporation answered 4/9, 2020 at 11:7 Comment(1)
sorry I didn't see your reply. could you please tell me why I need to do that? Also, what will be the else part of above if statements?Eburnation
T
0

I ran into the same problem, no http response after adding the ContentCachingResponseWrapper. It was an order issue, once I changed the order of the calls, everything work as expected. The next block is the wrong way, causing the symptoms

ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
wrappedResponse.copyBodyToResponse();
filterChain.doFilter(wrappedRequest, wrappedResponse);

This is the order that resolved my issue.

ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
filterChain.doFilter(wrappedRequest, wrappedResponse);
wrappedResponse.copyBodyToResponse();
Tetroxide answered 31/1, 2023 at 21:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.