web-dev-qa-db-ja.com

ContentCachingResponseWrapperが空の応答を生成する

Spring MVCアプリケーションで要求と応答を記録するためのフィルターを実装しようとしています。私は次のコードを使用します:

@Component
public class LoggingFilter extends OncePerRequestFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

        LOGGER.debug(REQUEST_MESSAGE_FORMAT, requestWrapper.getRequestURI(), requestWrapper.getMethod(), requestWrapper.getContentType(),
                new ServletServerHttpRequest(requestWrapper).getHeaders(), IOUtils.toString(requestWrapper.getInputStream(), UTF_8));

        filterChain.doFilter(requestWrapper, responseWrapper);

        LOGGER.debug(RESPONSE_MESSAGE_FORMAT, responseWrapper.getStatus(), responseWrapper.getContentType(),
                new ServletServerHttpResponse(responseWrapper).getHeaders(), IOUtils.toString(responseWrapper.getContentInputStream(), UTF_8));
    }
}

そのため、リクエストを受け取り、期待どおりにログを記録しました。ログは次のとおりです。

2016-10-08 19:10:11.212 DEBUG 11072 --- [qtp108982313-19] by.kolodyuk.logging.LoggingFilter
----------------------------
ID: 1
URI: /resources/1
Http-Method: GET
Content-Type: null
Headers: {User-Agent=[curl/7.41.0], Accept=[*/*], Host=[localhost:9015]}
Body: 
--------------------------------------
2016-10-08 19:10:11.277 DEBUG 11072 --- [qtp108982313-19] by.kolodyuk.logging.LoggingFilter
----------------------------
ID: 1
Response-Code: 200
Content-Type: application/json;charset=UTF-8
Headers: {}
Body: {"id":"1"}
--------------------------------------

ただし、空の応答が返されます。 curlからの出力は次のとおりです。

$ curl http://localhost:9015/resources/1 --verbose
*   Trying ::1...
* Connected to localhost (::1) port 9015 (#0)
> GET /resources/1 HTTP/1.1
> User-Agent: curl/7.41.0
> Host: localhost:9015
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 08 Oct 2016 17:10:11 GMT
< Content-Type: application/json;charset=UTF-8
< Content-Length: 0
<
* Connection #0 to Host localhost left intact

何か案は?

ありがとう

26
StasKolodyuk

数時間の苦労の末、ようやく解決策を見つけました。

つまり、フィルターメソッドの最後でContentCachingResponseWrapper.copyBodyToResponse()を呼び出す必要があります。

ContentCachingResponseWrapperは、応答出力ストリームから読み取ることにより、応答本文をキャッシュします。そのため、ストリームは空になります。出力ストリームに応答を書き戻すには、ContentCachingResponseWrapper.copyBodyToResponse()を使用する必要があります。

39
StasKolodyuk

最後に問題を解決しました。これが完璧なソリューションです。

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.Apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStreamReader;
import Java.net.URI;
import Java.util.Enumeration;
import Java.util.Map;

import static Java.nio.charset.StandardCharsets.UTF_8;
import static net.logstash.logback.marker.Markers.appendFields;

@Component
public class LoggingFilter extends OncePerRequestFilter {

  private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class);

  @Autowired
  private ObjectMapper objectMapper;

  @Override
  protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

    ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(httpServletRequest);
    ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpServletResponse);

    filterChain.doFilter(requestWrapper, responseWrapper);

    String requestUrl = requestWrapper.getRequestURL().toString();
    HttpHeaders requestHeaders = new HttpHeaders();
    Enumeration headerNames = requestWrapper.getHeaderNames();
    while (headerNames.hasMoreElements()) {
      String headerName = (String) headerNames.nextElement();
      requestHeaders.add(headerName, requestWrapper.getHeader(headerName));
    }
    HttpMethod httpMethod = HttpMethod.valueOf(requestWrapper.getMethod());
    Map<String, String[]> requestParams = requestWrapper.getParameterMap();

    String requestBody = IOUtils.toString(requestWrapper.getInputStream(),UTF_8);
    JsonNode requestJson = objectMapper.readTree(requestBody);

    RequestEntity<JsonNode> requestEntity = new RequestEntity<>(requestJson,requestHeaders, httpMethod, URI.create(requestUrl));
    LOGGER.info(appendFields(requestEntity),"Logging Http Request");


    HttpStatus responseStatus = HttpStatus.valueOf(responseWrapper.getStatusCode());
    HttpHeaders responseHeaders = new HttpHeaders();
    for (String headerName : responseWrapper.getHeaderNames()) {
      responseHeaders.add(headerName, responseWrapper.getHeader(headerName));
    }
    String responseBody = IOUtils.toString(responseWrapper.getContentInputStream(), UTF_8);
    JsonNode responseJson = objectMapper.readTree(responseBody);
    ResponseEntity<JsonNode> responseEntity = new ResponseEntity<>(responseJson,responseHeaders,responseStatus);
    LOGGER.info(appendFields(responseEntity),"Logging Http Response");
    responseWrapper.copyBodyToResponse();
  }
}
6
Yashdeep Sharma