Spring - Modify response headers after controller processing
While dealing with Spring MVC, I had the wonderful opportunity of dealing with servlet filters. One thing we needed to accomplish was adding a header after the controller does the processing. It can be done in the controller method, right? But we needed to apply this modification to every incoming request. So, let’s start by implementing a new interceptor:
public class DummyInterceptor extends HandlerInterceptorAdapter {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
response.addHeader("dummy-header, "dummy-value");
}
}
This should do the trick, we thought. Well, no. Following filter does not work also:
@Component
public class DummyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} finally {
response.addHeader("dummy-header, "dummy-value");
}
}
}
The reason is as Spring document states:
Note that the postHandle method of HandlerInterceptor is not always ideally suited for use with @ResponseBody and ResponseEntity methods. In such cases an HttpMessageConverter writes to and commits the response before postHandle is called which makes it impossible to change the response, for example to add a header. Instead an application can implement ResponseBodyAdvice and either declare it as an @ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.
The response is committed before we can put hands on it. We couldn’t manage to
succeed with ControllerAdvice
and ResponseBodyAdvice
. Our case was a little
specific, we only needed to modify the response headers when a specific status
code is returned from the controller. ResponseBodyAdvice
does not provide the
processed status code. The simple header modification with ResponseBodyAdvice
is as follows:
@ControllerAdvice
public class HeaderModifierAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
response.getHeaders().add("dummy-header","dummy-value");
return body;
}
}
So, after some time with
SO,
the suggested code was successful for us. It simply wraps the
HttpServletResponse
object to add headers when status code is set. To modify
the response headers when a specific status code is returned is as follows:
@Component
public class DummyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
@Override
public void setStatus(int sc) {
super.setStatus(sc);
handleStatus(sc);
}
@Override
@SuppressWarnings("deprecation")
public void setStatus(int sc, String sm) {
super.setStatus(sc, sm);
handleStatus(sc);
}
@Override
public void sendError(int sc, String msg) throws IOException {
super.sendError(sc, msg);
handleStatus(sc);
}
@Override
public void sendError(int sc) throws IOException {
super.sendError(sc);
handleStatus(sc);
}
private void handleStatus(int code) {
if(code == 404)
addHeader("dummy-header, "dummy-value");
}
};
filterChain.doFilter(request, wrapper);
}
}
The number of headers can be expanded. Not all the overrides are necessary but they are included for edge cases.