简体   繁体   中英

HandlerInterceptor getOutputStream() has already been called for this response

I am using Spring @RestController & sending response in Json format. That is working fine but i got requirement to send response to logger so i have implemented HandlerInterceptor of Spring and

@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());
}    

getting exception

--afterCompletion method executed-- http://localhost:8080/login 2015-11-29 05:48:29.184 ERROR 9116 --- [nio-8080-exec-1] osweb.servlet.HandlerExecutionChain : HandlerInterceptor.afterCompletion threw exception

java.lang.IllegalStateException: getOutputStream() has already been called for this response at org.apache.catalina.connector.Response.getWriter(Response.java:578) at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) at to.lookup.api.logger.LoggingInterceptor.afterCompletion(LoggingInterceptor.java:32)

You cannot do this in a spring interceptor because, when dealing with response's OutputStream , once you read a portion or a stream, its "consumed" and there no way to go back and read it again.

To enable the re-reads on a request, or a response, you would have to wrap the them and do so in the servlet filter, and than propagate the wrapped implementations down the filter chain.

You can find an already implemented wrappers that you can learn from, eg spring-mvc-logger . The implementation is based on the TeeInputStream which copies all the bytes read from OutputStream to a secondary OutputStream which allows re-reads.

Posting the code of the ResponseWrapper class used

   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;
    }
}

afterCompletion is called after completion of request processing, that is, after rendering the view. Once the view is rendered, you cant use response.getWriter and hence you are getting the exception. What you can do is implement postHandle, which is called before the view rendering. If you still get error about response already being used then try following as well.

Add below config to web.xml

<jsp-config>
  <jsp-property-group>
    <url-pattern>*.jsp</url-pattern>
    <trim-directive-whitespaces>true</trim-directive-whitespaces>
  </jsp-property-group>
</jsp-config> 

If the response is created in servlet, add below to servlets init param in web.xml

<init-param>
  <param-name>trimSpaces</param-name>
  <param-value>true</param-value>
</init-param>

For the logging of response, you might want to take a look at this .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM