spring http request body 복사 방법 문의

5,808 views
Skip to first unread message

namkyu Lee

unread,
Mar 27, 2014, 2:37:56 AM3/27/14
to ks...@googlegroups.com


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

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

interceptor에서 이미 inputstream안에 있는 데이터를 읽어 들였기 때문에 나오는 증상인 것으로 보입니다.

인터셉터와 컨트롤러 모두에서 request 의 body 데이터를 읽어 들이고 싶을 때 어떤 방법을 이용해야 하나요?


http request post (json) =====> interceptor =============> spring controller
body stream read             body stream read (오류)

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

코바(이수홍)

unread,
Mar 27, 2014, 8:03:38 AM3/27/14
to ks...@googlegroups.com
어떻게 바디를 읽었는지 코드가 궁금하네요.
혹시 filecopy utils 사용하셨나요?

namkyu Lee

unread,
Mar 27, 2014, 8:19:20 PM3/27/14
to ks...@googlegroups.com
interceptor에서는 jackson 라이브러리를 사용하여 다음과 같이 body를 읽어 오도록 되어 있습니다.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
ConstraintData data = mapper.readValue(inputStream, ConstraintData.class);

위와 같이 interceptor에서 body 데이터를 읽은 후 Controller에 진입하게 되면 stream을 이용하여 이미 데이터를
읽어 들였기 때문에 예외가 발생하고 있는 상황입니다.

정상혁

unread,
Mar 27, 2014, 10:27:19 PM3/27/14
to ks...@googlegroups.com
Filter내부에서 HttpServletRequestWrapper를 상속한 객체를 새로 생성해서 FilterChain.doFilter로 넘겨야할 것 같습니다..

조금 다른 사례이지만 아래 코드가 유사한 방식으로 Filter내부에서 새로 Request를 생성했습니다.


https://gist.github.com/benelog/4212252



구글을 찾아보니 질문하신, Fileter에서 input stream을 읽은 경우에도 그렇게 처리한 사례가 보이네요.


http://choong0121.tistory.com/220

권남

unread,
Mar 27, 2014, 11:04:48 PM3/27/14
to ks...@googlegroups.com
validation 타이밍을 filter/interceptor 보다는 Controller 호출 직전 시점 
으로 잡는것이 더 나아보입니다.
그렇다면 별다른 작업없이 이미 변환된 JSON을 그대로 받아서 처리할 수 있을 
듯 하네요.

AOP를 사용하던가 하면 될듯하네요.


근데 이제 이메일을 통한 포스팅은 안되는건가요?? 계속 Delivery Failure가 일어나네요.

박용권

unread,
Mar 30, 2014, 9:45:26 PM3/30/14
to KSUG
@kwon37xi

구글 그룹스 형태를 조정하는 과정에서 이메일 게시에 대한 설정이 바뀐걸 확인했습니다.

지금 조정했으니 다시 이메일 게시가 될거에요. :3

불편함을 드려서 죄송해요- 


2014년 3월 28일 오후 12:04, 권남 <kwon...@gmail.com>님이 작성:

--
이 메일은 Google 그룹스 'Korea Spring User Group Q&A' 그룹에 가입한 분들에게 전송되는 메시지입니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 ksug+uns...@googlegroups.com에 이메일을 보내세요.
http://groups.google.com/group/ksug에서 이 그룹을 방문하세요.
웹에서 이 토론을 보려면 https://groups.google.com/d/msgid/ksug/4f466e49-cdc2-49b5-8c48-b6d80855cad6%40googlegroups.com을(를) 방문하세요.

더 많은 옵션을 보려면 https://groups.google.com/d/optout을(를) 방문하세요.

namkyu Lee

unread,
Mar 31, 2014, 6:54:05 AM3/31/14
to ks...@googlegroups.com
답변이 좀 늦었습니다.

상혁님께서 말씀하신 것 처럼 
request body 데이터를 읽어 들여 저장해 놓는 객체를 생성해 놓은 후 이를 doFilter로 넘기는 것으로 해결하였습니다.

정답을 알고 나니 해답은 의외로 간단하네요.
HTTP request body 데이터를 읽어 들여 byte[]로 저장한 뒤 request.getInputStream 할 때마다 body[] 배열에 있는 값을 매번 읽어 들이면 되는 거였네요.


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

===============================

public class HttpRequestWrapper extends HttpServletRequestWrapper {

/** HTTP request body data */
private byte[] bodyData;

/**
* @param request
* @throws IOException
*/
public HttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream is = super.getInputStream();
bodyData = IOUtils.toByteArray(is);
}

/**
* <pre>
* getInputStream
*
* <pre>
* @return
* @throws IOException
*/
@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);
}

}



@권남님

AOP를 이용하여 처리를 해볼까라는 생각을 했었지만 URI 패턴에 따라서 validation 체크를 해야 했고, interceptor 빈 초기화 시 DI로 주입해 주는 String 값들이 많아 배제하고 있었습니다. ㅠㅠ
다행히 HttpServletRequestWrapper를 재구현 하는 방법으로 문제를 해결하였습니다.


도움 주신 분들께 감사드립니다. (그나저나 상혁님, 권남님께서 답변을 달아주시니 예전 스프링 스터디 했던 시절이 생각나네요. ^^ 잘 지내고 계시죠?)




2014년 3월 31일 오전 10:45, 박용권 <araw...@gmail.com>님이 작성:
Reply all
Reply to author
Forward
0 new messages