Java 在过滤器中添加响应头?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32829124/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:09:50  来源:igfitidea点击:

Adding header in response in filter?

javaspring-mvcfilterhttpresponseservlet-filters

提问by emilly

I need to add the header in each response. I am planning to do below

我需要在每个响应中添加标题。我打算在下面做

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", "*"); 
    }

}

I would like to do it after filterChain.doFilter(request, response)so that once controller process it, i just add header before returning to client. Is it correct ?

我想之后再做,filterChain.doFilter(request, response)以便一旦控制器处理它,我只需在返回客户端之前添加标头。这是正确的吗 ?

But As per How to write response filter?

但是根据如何编写响应过滤器?

After chain.doFilterhas 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.

之后chain.doFilter又回来了,这是来不及做的任何回应。此时,整个响应已经发送到客户端,您的代码无法访问它。

Above statement does not look right to me. Can't i add header after filterChain.doFilter(request, response)? If not why ?

以上声明在我看来并不正确。我不能在后面添加标题filterChain.doFilter(request, response)吗?如果不是为什么?

i am using spring mvc.

我正在使用 spring mvc。

采纳答案by Luan Reffatti

After filterChain.doFilteris called it's too late to do anything with the response. At this point, the entire response was already sent to the client.

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

You need to build a wrap response into your own classes, pass these wrappers into doFiltermethod and handle any processing in your wrappers.

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

There is already a response wrapper: HttpServletResponseWrapperthat you can extend. For example:

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

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

Your filter:

您的过滤器:

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

回答by M Sach

From The Java EE Tutorial

来自 Java EE 教程

A filter that modifies a response must usually capture the response before it is returned to the client. To do this, you pass a stand-in stream to the servlet that generates the response. The stand-in stream prevents the servlet from closing the original response stream when it completes and allows the filter to modify the servlet's response.

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

To pass this stand-in stream to the servlet, the filter creates a response wrapper that overrides the getWriter or getOutputStream method to return this stand-in stream. The wrapper is passed to the doFilter method of the filter chain. Wrapper methods default to calling through to the wrapped request or response object. This approach follows the well-known Wrapper or Decorator pattern described in Design Patterns,

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

回答by Joey587

This is a little late, but the below might help some So if you really wanted to append values to an existing header, or add new values to an existing header, the best way possible to write a wrapper and set the value in the wrapper.

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

Then chain the response in the filter

然后在过滤器中链接响应

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

Here is the ByteArrayPrinter.java

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

And here is the ByteArrayServletOutputStream

这是 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

    }

}

回答by David Bala?ic

I use this in my project with Spring 3.0.x:

我在我的 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);
    }
}

Works fine.

工作正常。