在过滤器中添加 header 作为响应?

Adding header in response in filter?

我需要在每个回复中添加 header。我打算在下面做

public class MyFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        filterChain.doFilter(request, response);
            response.addHeader("Access-Control-Allow-Origin", "*"); 
    }

}

我想在 filterChain.doFilter(request, response) 之后做,这样一旦控制器处理了它,我就在返回之前添加 header 给客户。正确吗?

但根据 How to write response filter?

After chain.doFilter has returned, it's too late to do anything with the response. At this point, entire response was already sent to the client and your code has no access to it.

我觉得上面的说法不对。我不能在 filterChain.doFilter(request, response) 之后添加 header 吗?如果不是,为什么?

我正在使用 spring mvc。

调用 filterChain.doFilter 后,再对响应做任何事情都为时已晚。此时,整个响应已经发送到客户端。

您需要将包装响应构建到您自己的 类 中,将这些包装器传递到 doFilter 方法并处理包装器中的任何处理。

已经有一个可以扩展的响应包装器:HttpServletResponseWrapper。例如:

public class MyResponseRequestWrapper extends HttpServletResponseWrapper{
    public MyResponseRequestWrapper(HttpServletResponse response) {
        super(response);
    }
}

您的过滤器:

@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    HttpServletResponse myResponse = (HttpServletResponse) response;
    MyResponseRequestWrapper responseWrapper = new MyResponseRequestWrapper(myResponse);
    responseWrapper.addHeader("Access-Control-Allow-Origin", "*");
    filterChain.doFilter(request, myResponse);
}

From The Java EE Tutorial

修改响应的过滤器通常必须在响应 returned 到客户端之前捕获响应。为此,您将一个替代流传递给生成响应的 servlet。替代流防止 servlet 在完成时关闭原始响应流,并允许过滤器修改 servlet 的响应。

为了将此替代流传递给 servlet,过滤器创建了一个响应包装器,该包装器将 getWriter 或 getOutputStream 方法覆盖到 return 此替代流。包装器传递给过滤器链的 doFilter 方法。包装器方法默认调用包装的请求或响应对象。这种方法遵循设计模式中描述的众所周知的包装器或装饰器模式,

这有点晚了,但下面的内容可能会有所帮助 因此,如果您真的想将值附加到现有 header,或将新值添加到现有 header,最好的方法是编写包装器并在包装器中设置值。

然后在过滤器中链接响应

HttpServletResponse response = (HttpServletResponse) servletResponse;
ByteArrayPrinter pw = new ByteArrayPrinter();

// Create a wrapper
HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {

    @Override
    public void setContentType(final String type) {
        super.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
    }

    @Override
    public PrintWriter getWriter() {
        return pw.getWriter();
    }

    // set the outputstream content type to JSON
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        ServletResponse response = this.getResponse();

        String ct = (response != null) ? response.getContentType() : null;
        if (ct != null && ct.contains(APPLICATION_XHTML)) {
            response.setContentType(ct + AppConstants.CONSTANT_COMMA + MediaType.APPLICATION_JSON_UTF8_VALUE);
        }
        return pw.getStream();
    }

};
chain.doFilter(httpRequest, wrappedResp);

这是ByteArrayPrinter.java

public class ByteArrayPrinter {

    private ByteArrayOutputStream baos = new ByteArrayOutputStream();

    private PrintWriter pw = new PrintWriter(baos);

    private ServletOutputStream sos = new ByteArrayServletStream(baos);

    public PrintWriter getWriter() {
        return pw;
    }

    public ServletOutputStream getStream() {
        return sos;
    }

    byte[] toByteArray() {
        return baos.toByteArray();
    }
}

这里是 ByteArrayServletOutputStream

public class ByteArrayServletStream extends ServletOutputStream {

    ByteArrayOutputStream baos;

    ByteArrayServletStream(ByteArrayOutputStream baos) {
        this.baos = baos;
    }

    @Override
    public void write(int param) throws IOException {
        baos.write(param);
    }

    @Override
    public boolean isReady() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setWriteListener(WriteListener listener) {
        // TODO Auto-generated method stub

    }

}

我在我的项目中使用它 Spring 3.0.x:

public class MyFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
    {
        response.addHeader("headerName", "headerValue");
        filterChain.doFilter(request, response);
    }
}

工作正常。