2016-07-27 1 views
0

요청이 스프링 컨트롤러에 도달하기 전에 서블릿 필터에서 XML (POST 요청)을 가져와야하는 요구 사항에 대해 현재 작업 중입니다. 그런 다음 XML (빈 노드/요소 잘라 내기)을 처리해야합니다. 필터를 누른 다음 호출이 더 진행되어야합니다.POST 요청에서 XML을 가져와 서블릿 필터에서 수정하는 방법은 무엇입니까?

아래 코드 (첨부 된 스 니펫)를 시도하고 요청 본문 (XML)을 가져올 수 있었으며 수정 된 응답을 설정할 수있었습니다.

HttpServletRequest httpRequest = (HttpServletRequest) request; 
    HttpServletResponse httpResponse = (HttpServletResponse) response; 
    if (httpRequest.getMethod().equalsIgnoreCase("POST")) { 
     extractDataFromRequest(httpRequest); 
     httpResponse.getWriter().write("<root><root>"); 
    } 
    chain.doFilter(request, wrappedResponse); 

    public static String extractDataFromRequest(HttpServletRequest request) throws IOException { 

    String line; 
    StringBuilder builder = new StringBuilder(); 
    BufferedReader reader = request.getReader(); 
    while ((line = reader.readLine()) != null) { 
     builder.append(line); 
    } 
    return builder.toString(); 
    } 

그러나 스프링이 다음 예외를 제외하고 실패했습니다.

Severe: java.lang.IllegalStateException: PWC3997: getReader() has already been called for this request 
    at org.apache.catalina.connector.Request.getInputStream(Request.java:1178) 
    at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:407) 
    at org.springframework.http.server.ServletServerHttpRequest.getBody(ServletServerHttpRequest.java:165) 
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:120) 
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:100) 

전문가로부터이 요구 사항에 대한 구체적인 구현을 찾고 있습니다.

답변

2

InputStream을 두 번 사용할 수 없으므로 반복적 인 InputStream 복사본을 유지하는 래퍼 클래스를 만들어야합니다.

public class ReadTwiceHttpServletRequestWrapper extends HttpServletRequestWrapper { 

private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 

public ReadTwiceHttpServletRequestWrapper(HttpServletRequest request) { 
    super(request); 
    try { 
     IOUtils.copy(request.getInputStream(), outputStream); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

@Override 
public BufferedReader getReader() throws IOException { 
    return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray()))); 
} 

@Override 
public ServletInputStream getInputStream() throws IOException { 
    final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); 
    return new ServletInputStream() { 

     @Override 
     public int readLine(byte[] b, int off, int len) throws IOException { 
      return inputStream.read(b, off, len); 
     } 

     @Override 
     public boolean isFinished() { 
      return inputStream.available() > 0; 
     } 

     @Override 
     public boolean isReady() { 
      return true; 
     } 

     @Override 
     public void setReadListener(ReadListener arg0) { 
      // TODO Auto-generated method stub 
     } 

     @Override 
     public int read() throws IOException { 
      return inputStream.read(); 
     } 
    }; 
} 

public void setBody(String body) { 
    outputStream = new ByteArrayOutputStream(); 
    try { 
     outputStream.write(body.getBytes()); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

public String getBody() { 
    return new String(outputStream.toByteArray()); 
} 

은} 그런 다음 체인의 첫 번째 필터로 그를 초기화해야합니다.

public class ReadTwiceFilter implements Filter { 

@Override 
public void destroy() { 
} 

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

    ReadTwiceHttpServletRequestWrapper readTwiceHttpServletRequestWrapper = new ReadTwiceHttpServletRequestWrapper(
      (HttpServletRequest) request); 

    String newBody = readTwiceHttpServletRequestWrapper.getBody().replace("<soap:studentId>1</soap:studentId>", "<soap:studentId>2</soap:studentId>"); 
    readTwiceHttpServletRequestWrapper.setBody(newBody); 

    chain.doFilter(readTwiceHttpServletRequestWrapper, response); 
} 

@Override 
public void init(FilterConfig arg0) throws ServletException { 
} 

} 정보에 대한

+0

감사합니다. 그러나 응답의 수정 된 내용을 설정하는 방향을 얻으면 좋을 것입니다. 그래서 스프링 컨트롤러에서 수정 된 내용을 얻을 수 있습니다. – JayP

+0

예제를 업데이트했습니다. 여기서 요청 본문을 변경 한 다음 계속합니다. Spring Interceptor를 사용하여이를 수행 할 수도 있습니다. –

0

getReader 메서드 대신 getInputStream을 사용하도록 필터에서 구현을 변경하십시오. 이 문제는 전체 구현이 ServletRequest에서 getReader 및 getInputStream 메소드를 호출 할 때 발생합니다.

javadoc에서 언급했듯이 그 중 하나만 호출 할 수 있습니다. stacktrace보고; 컨트롤러 (spring mvc)가 getInputStream을 호출하여 메시지로 실패했습니다. getReader() has already been called...

관련 문제