2016-07-27 46 views
0

我目前正在处理一个需求,在请求到达Spring控制器之前,我需要在servlet过滤器中获取XML(来自POST请求),然后我需要处理XML(切断一些空的节点/元素)在过滤器中,然后该呼叫应该继续进行。如何从POST请求中获取XML并在Servlet过滤器中对其进行修改?

我试着下面的代码(附带只是片段),我能够得到请求正文(XML),并能够设置修改后的响应。

​​

但是,弹簧失败,出现以下例外。

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

在筛选器中更改您的实施,以使用getInputStream而不是getReader方法。当整体实现调用ServletRequest的getReader和getInputStream方法时,会出现此问题。

javadoc所述,只能调用其中的一个。看着堆栈跟踪;控制器(spring mvc)正在调用getInputStream并因此失败并显示消息getReader() has already been called...

相关问题