2016-10-08 3 views
15

응용 프로그램에서 요청 및 응답을 기록하기위한 필터를 구현하려고합니다.ContentCachingResponseWrapper가 빈 응답을 생성합니다.

@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)); 
    } 
} 

그래서, 내 요청 및 respone 예상대로 기록을 얻을 : 는 다음 코드를 사용합니다. 다음은 로그입니다.

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 

아이디어가 있습니까?

감사합니다.

답변

18

몇 시간의 고생 끝에 결국 해결책을 찾았습니다.

즉, ContentCachingResponseWrapper.copyBodyToResponse()은 필터 메소드의 끝에서 호출되어야합니다.

ContentCachingResponseWrapper은 응답 출력 스트림에서 응답 본문을 읽어 캐시합니다. 따라서 스트림이 비게됩니다. 출력 스트림에 응답을 다시 작성하려면 ContentCachingResponseWrapper.copyBodyToResponse()을 사용해야합니다.

+2

그들은 당신이 자동으로 필터를 등록합니다이 필터 – lpandzic

1

마지막으로 문제가 해결되었습니다. 여기에 완벽한 솔루션입니다 :

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(); 
    } 
} 
+0

의 javadoc는이를 추가해야합니다. – user2478236

+1

@Component 어노테이션을 등록 않았다 어떻게 ContentCachingResponseWrapper –

+0

이 메소드에도 빈 "String requestBody"가 있습니다 ... –

관련 문제