【问题标题】:HandlerInterceptor getOutputStream() has already been called for this response已经为此响应调用了 HandlerInterceptor getOutputStream()
【发布时间】:2016-03-03 03:20:34
【问题描述】:

我正在使用 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 错误 9116 --- [nio-8080-exec-1] os.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)

【问题讨论】:

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


    【解决方案1】:

    您不能在 spring 拦截器中执行此操作,因为在处理响应的 OutputStream 时,一旦您读取了一部分或流,它就会“被消耗”并且无法返回并再次读取它。

    要对请求或响应启用重新读取,您必须将它们包装并在 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;
        }
    }
    

    【讨论】:

    • 完全正确。我在我的回答中也添加了类似解决方案的链接
    • 太好了,很高兴它有帮助
    【解决方案2】:

    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>
      <param-name>trimSpaces</param-name>
      <param-value>true</param-value>
    </init-param>
    

    对于响应记录,您可能需要查看this

    【讨论】:

    • 在 postHandle 中获取异常,但它现在是一个不同的异常,我尝试刷新但它仍然在获取。 java.lang.IllegalStateException: getOutputStream() 已经在 org.apache.catalina.connector.Response.getWriter(Response.java:578) at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade. java:212) 在 to.lookup.api.logger.LoggingInterceptor.postHandle(LoggingInterceptor.java:25)
    • 没有给出相同的异常我正在使用 spring boot 实现但仍然得到异常@Bean public ServletContextInitializer initializer() { return new ServletContextInitializer() { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext. setInitParameter("trimSpaces", "true"); } }; }
    猜你喜欢
    • 1970-01-01
    • 2017-07-17
    • 2023-03-08
    • 1970-01-01
    • 2020-01-08
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多