已经为此响应调用了HandlerInterceptor getOutputStream()

时间:2015-11-29 11:59:13

标签: java spring-mvc servlets exception-handling spring-boot

我正在使用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)

2 个答案:

答案 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