5

Lets say I have an Spring REST API which has many, many responses being returned throughout the code.

If I wanted to return two specific headers with every single response I send out, How might I do that in a more intelligent way than manually adding them to every response before returning?

Is there a mechanism which allows me to catch the response before I send it, and add the headers?

EDIT : For future visitors asking this question. None of the answers here will actually result in a working interceptor. I suggest looking elsewhere.

IWishIWasABarista
  • 413
  • 2
  • 4
  • 19
  • You need an interceptor. [This question](http://stackoverflow.com/questions/38360215/how-to-create-a-spring-interceptor-for-spring-restful-web-services) may be of interest to you. The `postHandle()` method is probably more appropriate for what you are trying to accomplish – rmlan Apr 18 '17 at 20:27

5 Answers5

15

The correct answer is to use a filter. Interceptors are not correct for this, regardless what people online say. Interceptors just don't work as desired.

Working solution is to create a filter as follows :

public class myAwesomeFilter extends OncePerRequestFilter {

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

        response.addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.addHeader("pragma", "no-cache");
        filterChain.doFilter(request, response);
    }
}

Then, in web.xml - you need the following :

<filter>
    <filter-name>sensitiveFormHeaderFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
IWishIWasABarista
  • 413
  • 2
  • 4
  • 19
  • 2
    From the docs: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-handlermapping-interceptor - "Note that postHandle is less useful with @ResponseBody and ResponseEntity methods for which the response is written and committed within the HandlerAdapter and before postHandle. That means it is too late to make any changes to the response, such as adding an extra header." – Matty Sep 30 '19 at 15:17
  • But with a preHandle interceptor (rather then a postHandle as suggested above) you can still add headers to all requests if you want. – Matty Sep 30 '19 at 15:18
  • but in this way I can add filters only before request handling, if I need to add it after? – firegloves Feb 04 '20 at 14:53
  • 2
    You can define the filter as @Component - no need for web.xml – Alexey Stepanov Aug 31 '20 at 16:15
8

The way I got it to work was using ResponseBodyAdvice as shown in the example linked by Faxy.

Here is my solution for adding cache-control headers to every request:

@ControllerAdvice
public class HeaderModifierAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(final MethodParameter returnType, final Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(final Object body,
                                  final MethodParameter returnType,
                                  final MediaType selectedContentType,
                                  final Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  final ServerHttpRequest request,
                                  final ServerHttpResponse response) {
        response.getHeaders().add("Cache-Control", "no-cache, no-store, must-revalidate");
        response.getHeaders().add("Pragma", "no-cache");
        response.getHeaders().add("Expires", "0");
        return body;
    }
}
AllanT
  • 923
  • 11
  • 23
  • 2
    I think this solution will work only if your controller methods are returning an object. Any controller method that returns 404, 201, 204 status and no content will not have the above headers. – Vijay Kalidindi Aug 13 '17 at 14:35
  • This method will also not work for controllers that return modelandview – Gorky Aug 15 '17 at 03:33
  • 1
    But you should probably not try to do that in general, it will fail if you try to do that when returning HttpEntity/ResponseEntity from the controller method because HttpEntity makes its headers read-only. – anre Sep 21 '17 at 18:44
  • 1
    Confirming that it does not work for 201 as pointed by @VijayKalidindi – Adi Dec 04 '18 at 21:44
1

You can easily do those using Handler Interceptors which allow you to modify the request processing lifecycle within Spring MVC. Interceptors are a very powerful tool that allows us to add functionality to the request processing lifecycle at 3 different points:

  1. before a controller handles a request
  2. after a controller method completed its code execution
  3. when the view is about to be rendered and sent back as the response to the client

I think option 2 will suit your needs.

Then you can write something like this:

public class MyInterceptor extends HandlerInterceptorAdapter {

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);

        //add your two headers on the response here
    }


}

step 2 is to register that interceptor in your configuration file, add the next lines to your XML configuration:

<mvc:interceptors>
   <bean Class="your interceptor class">
</mvc:interceptors>

from now on that interceptor will apply for every request.

Moshe Arad
  • 3,587
  • 4
  • 18
  • 33
  • This might sound a bit dumb - but where do I put the XML? Is there a specific file it belongs in? – IWishIWasABarista Apr 19 '17 at 16:02
  • You suppose to create a Spring Bean XML Configuration file and place under resources folder, but that's not enough, you also need to load it explicitly into Spring IOC container – Moshe Arad Apr 19 '17 at 16:14
  • Ok, so the Spring Bean XML configuration, I get - but what is the IOC container? Sorry about this, the XML config side of this baffles me. – IWishIWasABarista Apr 19 '17 at 16:37
  • it seems that you need to learn about spring a little bit more from the theory perspective, i would suggest first to read a few books or to learn from videos. The IOC container is the Spring way to implement the Dependency injection and the container is where your beans exist and being managed. – Moshe Arad Apr 19 '17 at 16:41
  • I know I could learn a lot more about Spring - however, I am just covering for a java developer who is off this week and next. I am a C++ developer normally - I just need to get this working in the meantime. – IWishIWasABarista Apr 19 '17 at 16:45
  • If that was helpful, a vote up will be appreciated, Cheers ;-) – Moshe Arad Apr 19 '17 at 16:47
  • I can't mark it as the answer just yet - the information I have here doesn't result in a working interceptor. If someone comes here with the same problem as me, this will not work for them. – IWishIWasABarista Apr 19 '17 at 16:49
  • If your request is already committed then you won't be able to add headers anymore. You could override the `preHandle` in that case. – Matty Sep 30 '19 at 15:19
-1

You may use spring interceptors for this purpose. Or there is more generic way to do this which not requires Spring for this. It's filter

-2

I believe you can implement the HandlerInterceptorAdapter interface and override the postHandle() method.

example

Faxy
  • 17
  • 2