RequestBody의 내용을 로그로 남기고 싶다(2)

이미 일년도 넘은 내용이지만 에버노트에서 꺼내보면서 이것 저것 정리도 해볼겸 RequestBody의 내용을 로그로 남기고 싶다. 에 이어서 좀 더 관련 내용을 적어보았다. filter 를 이용해서 request body 를 로그로 찍어보자.

우선 문자열과 input stream 의 편리한 처리를 위해서 다음과 같이 의존성을 추가한 다음에 진행하였다.

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>

테스트를 위해서 다음과 같이 간단하게 post 방식의 api를 하나 생성하고, Parameter 클래스를 정의했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Parameter {
private String message;
public void setMessage(String message) {
this.message = message;
}
}

@RestController
public class TestController {

@PostMapping("/test")
public Boolean postTest(@RequestBody Parameter body) {
return Boolean.TRUE;
}
}

Filter 인터페이스를 구현하여 ReadableRequestWrapperFilter 클래스를 만들어주었다. ReadableRequestWrapper 에서 요청에 대한 내용들을 읽어서 출력해볼 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ReadableRequestWrapperFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ReadableRequestWrapper wrapper = new ReadableRequestWrapper((HttpServletRequest) request);
chain.doFilter(wrapper, response);
}

@Override
public void destroy() {

}
}

HttpServletRequestWrapper 을 상속받은 ReadableRequestWrapper 클래스를 다음과 같이 만들었다.

1
2
3
4
5
6
7
8
9
10
11
12
public class ReadableRequestWrapper extends HttpServletRequestWrapper{
public ReadableRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try {
InputStream is = request.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
System.out.println("----- input " + bufferedReader.lines().collect(Collectors.joining(System.lineSeparator())));
} catch (Exception e) {
throw e;
}
}
}

http request에서 input stream을 읽어 들인 다음, 해당 내용을 출력하도록 했다. ----- input {"message":"test"} 와 같이 결과가 출력된다.

1
2018-10-23 07:08:06.635  WARN 7936 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.Boolean com.daeuky.springboot.mybootapp.TestController.postTest(com.daeuky.springboot.mybootapp.Parameter)

그러나 위의 메시지에서 확인 할 수 있듯이 request body가 사라져서 @RequestBody 로 선언해준 Parameter 객체가 생성되지 않는다. 기본적으로 스트림은 한 번 읽는게 원칙이다.

ReadableRequestWrapper 클래스에 버퍼 byte[] rawData 를 정의한 다음 input stream 을 읽어 스트림을 byte[] 타입으로 버퍼에 저장했다. getInputStream() 을 오버라이딩 하여 input stream 읽기 요청이 들어오는 경우, 버퍼에 저장해둔 내용을 반환해준다. getReader() 메소드를 오버라이딩하여 버퍼의 내용을 BufferedReader 타입으로 반환한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ReadableRequestWrapper extends HttpServletRequestWrapper {

private final Charset encoding;
private byte[] rawData;

public ReadableRequestWrapper(HttpServletRequest request) throws IOException {
super(request);

String charEncoding = request.getCharacterEncoding();
this.encoding = StringUtils.isBlank(charEncoding) ? StandardCharsets.UTF_8 : Charset.forName(charEncoding);

try {
InputStream is = request.getInputStream();
this.rawData = IOUtils.toByteArray(is);

System.out.println("----- input " + this.getReader().lines().collect(Collectors.joining(System.lineSeparator())));
} catch (IOException e) {
throw e;
}
}

@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.rawData);
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
};

return servletInputStream;
}

@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream(), this.encoding));
}
}

테스트를 위해서 curl 을 날려보았다.

1
$ curl -XPOST -H "Content-Type: application/json" -d '{"message":"test"}' http://localhost:8080/test

최초에 확인했던 로그 ----- input {"message":"test"} 가 정상적으로 노출되고, controller에도 정상적으로 파라미터가 전달되어 Parameter 객체에서도 내용을 확인 할 수 있다.

JPA 특징 카프카 컨슈머

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×