我正在使用Spring @RestController
&以Json格式发送响应。这工作正常,但我要求发送响应记录器所以我已经实现了Spring的HandlerInterceptor和
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("--afterCompletion method executed--"+ request.getRequestURL());
System.out.println("--afterCompletion method executed--"+ response.getWriter());
}
获得异常
- 执行afterCompletion方法 - http://localhost:8080/login 2015-11-29 05:48:29.184 ERROR 9116 --- [nio-8080-exec-1] o.s.web.servlet.HandlerExecutionChain:HandlerInterceptor.afterCompletion引发异常
java.lang.IllegalStateException:getOutputStream()已经存在 呼吁这个回应 org.apache.catalina.connector.Response.getWriter(Response.java:578) 在 org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) 在 to.lookup.api.logger.LoggingInterceptor.afterCompletion(LoggingInterceptor.java:32)
答案 0 :(得分:1)
你不能在弹簧拦截器中这样做,因为在处理响应的OutputStream
时,一旦你读了一个部分或一个流,它就会消耗掉#34;并且没有办法再回过头来阅读它。
要启用对请求或响应的重新读取,您必须将它们包装起来并在servlet过滤器中执行,然后将包装的实现传播到过滤器链中。
您可以找到已经实现的包装器,您可以从中学习,例如spring-mvc-logger。该实现基于TeeInputStream,它将从OutputStream
读取的所有字节复制到允许重新读取的辅助OutputStream
。
发布使用的ResponseWrapper
类的代码
public class ResponseWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
private PrintWriter writer = new PrintWriter(bos);
private long id;
public ResponseWrapper(Long requestId, HttpServletResponse response) {
super(response);
this.id = requestId;
}
@Override
public ServletResponse getResponse() {
return this;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
private TeeOutputStream tee = new TeeOutputStream(ResponseWrapper.super.getOutputStream(), bos);
@Override
public void write(int b) throws IOException {
tee.write(b);
}
};
}
@Override
public PrintWriter getWriter() throws IOException {
return new TeePrintWriter(super.getWriter(), writer);
}
public byte[] toByteArray(){
return bos.toByteArray();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
答案 1 :(得分:-1)
在完成请求处理之后,即在渲染视图之后调用afterCompletion。呈现视图后,您无法使用response.getWriter,因此您将获得异常。你可以做的是实现postHandle,它在视图渲染之前调用。 如果您仍然收到有关已使用的响应的错误,请尝试关注。
将以下配置添加到web.xml
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>
如果在servlet中创建响应,请在web.xml
中添加以下servlet init param<init-param>
<param-name>trimSpaces</param-name>
<param-value>true</param-value>
</init-param>
要记录响应,您可能需要查看this。