如何从POST请求中获取XML并在Servlet Filter中修改它?

我目前正在处理一个需求,我需要在请求到达Spring控制器之前在servletfilter中获取XML(来自POST请求),然后我需要处理XML(切断一些空节点/元素)过滤然后调用应该继续进行。

我尝试了下面的代码(只附加了代码片段),我能够获取请求体(XML)并能够设置修改后的响应。

HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; if (httpRequest.getMethod().equalsIgnoreCase("POST")) { extractDataFromRequest(httpRequest); httpResponse.getWriter().write(""); } 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(); } 

但是,spring因以下exception而失败。

 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) 

我正在寻找专家对此要求的具体实施。

您不能两次使用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()); } 

}

然后你需要使用链中第一个filter来初始化它。

 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("1", "2"); readTwiceHttpServletRequestWrapper.setBody(newBody); chain.doFilter(readTwiceHttpServletRequestWrapper, response); } @Override public void init(FilterConfig arg0) throws ServletException { } 

}

在filter中更改您的实现以使用getInputStream而不是getReader方法。 当整个实现在ServletRequest上调用getReader和getInputStream方法时,就会出现此问题。

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