HTTP request body copy 방법

HTTP request 시 json 스트링을 서버에 넘겨주고, 서버는 interceptor에서 먼저 HTTP request body에 있는 json 스트링을 읽어와 값에 대한 체크를 진행하는 로직을 개발하고 있었다.


json 스트링 검증 후 spring controller에서 @RequestParam 으로 데이터를 받으려고 할 때 다음과 같은 예외가 발생

java.lang.RuntimeException: java.io.EOFException: No content to map to Object due to end of input


interceptor에서 request body의 데이터를 이미 읽어 들였기 때문에 나오는 증상이다.

시퀀스 다이어그램으로 표현하자면 다음의 4번에 해당하는 부분에서 예외가 발생된다.

etc-image-0



인터셉터와 컨트롤러 모두에서 request 의 body 데이터를 읽어 들이고 싶을 때 어떤 방법을 이용해야 할까 고민을 해봤지만 명확한 답을 찾을 수가 없어 KSUG에 질문을 올려 그 해답을 찾을 수 있었다.


해결 방법


해결 방법은 request body 데이터를 저장해 놓는 객체를 생성 후 이를 doFilter로 넘기는 것이였다.

말로 설명하는 것 보다는 코드를 예로 들면 다음과 같다.


## HttpRequestWrapperFilter.java

public class HttpRequestWrapperFilter implements Filter {

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

HttpServletRequest httpServletRequest = (HttpServletRequest) request;

HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpServletRequest);

chain.doFilter(requestWrapper, response);

}


## HttpRequestWrapper.java

public class HttpRequestWrapper extends HttpServletRequestWrapper {


private byte[] bodyData;

public HttpRequestWrapper(HttpServletRequest request) throws IOException {

super(request);

InputStream is = super.getInputStream();

bodyData = IOUtils.toByteArray(is);

}

@Override

public ServletInputStream getInputStream() throws IOException {

final ByteArrayInputStream bis = new ByteArrayInputStream(bodyData);

return new ServletImpl(bis);

}

}


class ServletImpl extends ServletInputStream {


private InputStream is;


public ServletImpl(InputStream bis) {

is = bis;

}


@Override

public int read() throws IOException {

return is.read();

}


@Override

public int read(byte[] b) throws IOException {

return is.read(b);

}


}