Adding header in response in filter?
Asked Answered
C

5

26

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 ?

But As per 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.

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

i am using spring mvc.

Chronometry answered 28/9, 2015 at 18:2 Comment(4)
@fps HandlerInterceptor doesn't allow modifying response in postHandle which is trigerred after the controller has processed the request. OP (and me too) wants to add a response header just before the response is sent to the client.Longtin
@fps I couldn't get it working even the filter for this use case where we want the header to be added after the request has passed through Controller. I used beforeBodyWrite method from implements ResponseBodyAdvice<Object>.Longtin
@Longtin What about the accepted answer? I really don't know the caveats, I haven't answeredBogoch
@Longtin Maybe this answer might be of helpBogoch
P
29

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

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

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

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);
}
Parent answered 28/9, 2015 at 19:29 Comment(4)
Why to use wrapper in your example instead of just adding header to original implementation passed by container? I think wrapper makes sense only if you implement some logic inside to make sure that your header wont be removed/overwritten somewhere down the filter chain.Epagoge
@Luan - in your answer above shouldn't you be passing responseWrapper to filterChain.doFilter?Perfidious
Thanks for the answer Luan!! In my case I wanted the header to be set only for 404 type of response, so as mentioned in the answer I added the below code in my wrapper class: @Override public void sendError(int sc) throws IOException { if (sc == 404) { this.setHeader("X-ServiceFabric", "ResourceNotFound"); } super.sendError(sc); }Perfidious
Why the unnecessary cast? Why the unnecessary variable myResponse? Why the wrapper? This all works with a simple: response.addHeader("headerName", "headerValue"); filterChain.doFilter(request, response);Encaustic
T
5

I use this in my project with 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.

Tenant answered 17/9, 2019 at 12:33 Comment(3)
> Give some additional hint on how/where to use "doFilterInternal" It is used automatically. IT is part of the OncePerRequestFilter interface.Encaustic
is it going to add the header before or after the request goes to the Controller?Longtin
@Longtin beforeEncaustic
V
2

From The Java EE Tutorial

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.

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,

Variant answered 29/9, 2015 at 12:42 Comment(0)
P
0

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

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

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

    }

}
Putup answered 26/12, 2018 at 11:16 Comment(0)
O
0

TL;DR

I'm most probably missing something obvious, but it seems the behaviour is different between Spring Boot 2.x and Spring Boot 3.x

Adding a custom header by extending the OncePerRequestFilter

  • does not work in Spring Boot 2.7
  • works in Spring Boot 3.1

On a side note, the location of HttpServletRequest and HttpServletResponse changed between 2.x and 3.x (See for example Why does spring-boot-3 give javax.servlet.http.HttpServletRequest ClassNotFoundException)

Details

Spring Boot 2.7.14

Filter class to add a custom header. Does NOT add the custom header.

package com.example.controller;


import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;   // notice the package
import javax.servlet.http.HttpServletResponse;  // notice the package
import java.io.IOException;

@Component
public class KeepAliveTimeoutFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        logger.info("Adding custom header"); // to confirm we're executing this
        response.addHeader("Foo", "Bar");
        filterChain.doFilter(request, response);
    }
}

Spring Boot 3.3.1

Filter class to add a custom header. ADDS the custom header.

package com.example.controller;


import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;     // notice the package
import jakarta.servlet.http.HttpServletResponse;    // notice the package
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class KeepAliveTimeoutFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        logger.info("Adding custom header"); // to confirm we're executing this
        response.addHeader("Foo", "Bar");
        filterChain.doFilter(request, response);
    }
}
Oberhausen answered 21/3 at 17:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.