tomcat 之filter

filter的基本功能大家都是清楚的,本节不赘述,本文主要讲述filter 是否可修改response及如何修改response

通常我们理解tomcat的调用流程是

clinet -> filter -> service 
                      |
clinet <- filter < - -

实际的处理流程是

clinet -> filter -> service 
                      |
          filter < - -
clinet <-   -   -    -

用postman测试就会发现,在filter resp时sleep的话,还没sleep结束,已经收到响应

tomcat会在servlet中write tcp报文 之后继续执行filter

如果我们想在filter中修改reponse报文,怎么办呢

如果直接使用response.getWriter().write()方法会报错getOutputStream() has already been called for this response

解决方案是可以封装新的response对象,在调用filterChain.doFilter(servletRequest, servletResponse);时传入,具体如下

public class ModifyResponseBodyWrapper extends HttpServletResponseWrapper {


    private ByteArrayOutputStream bos;

    public ModifyResponseBodyWrapper(HttpServletResponse response) {
        super(response);
        this.bos = new ByteArrayOutputStream();
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setWriteListener(WriteListener writeListener) {

            }

            @Override
            public void write(int b) throws IOException {
                bos.write(b);
            }

            @Override
            public void write(byte[] b) throws IOException {
                bos.write(b);
            }
        };
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(new OutputStreamWriter(bos));
    }

    public String getResponseBody() throws IOException {
        ServletOutputStream outputStream = this.getOutputStream();
        outputStream.flush();

        PrintWriter writer = this.getWriter();
        writer.flush();

        return bos.toString(this.getCharacterEncoding());
    }
}

这样在servlet中,会将write的内容缓存下来,在filter中真实write