Sunday, October 16, 2016

[JAVA,SPRING] Intercept and modify @ResponseBody before sending it back

So I got this idea to have a single common place to modify all my ResponseBody before sending it back to the caller. Initially I thought I can use Spring's HandlerInterceptor but I found out that ResponseBody can only be read once. Once it is read by HandlerInterceptor class, the ResponseBody will be gone. After much googling, I found out there are few solution to this. First is to implement a filter class. Another way is using ControllerAdvice classes which I will share below.

The Code

I'll start by sharing my ControllerAdvice below. Explanation after the code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestControllerAdvice(assignableTypes = MyRestController.class)
public class MyResponseControllerAdvice implements ResponseBodyAdvice<GenericResponseDocument<?>> {

    @Override
    public GenericResponseDocument<?> beforeBodyWrite(GenericResponseDocument<?> document, MethodParameter methodParam, MediaType mediaType,
            Class<? extends HttpMessageConverter<?>> converter, ServerHttpRequest request, ServerHttpResponse response) {

        //modify your document here
        ...

        return document;
    }

    @Override
    public boolean supports(MethodParameter methodParam, Class<? extends HttpMessageConverter<?>> converter) {

        if (ValidControllerMethodName.containsMethod(methodParam.getMethod().getName())) {
            return true;
        }

        return false;
    }

}

The Explanation

  • It is annotated with @RestControllerAdvice. It's basically same as @ControllerAdvice. Below is the explanation from spring docs.
  • On the @RestControllerAdvice, I've pass a value for assignableTypes. In this case MyRestController is the only controller class that I want MyResponseControllerAdvice class to intercept. You can ignore this, in which spring will try to intercept all controller classes.
  • It implements ResponseBodyAdvice, which accepts a generic class. In the example above I put it as GenericResponseDocument. However you can put it as any object type. The key is, it must be same as the object type of the @ResponseBody. Example of my controller method below. Notice that the return type is same as the generic class in the on my ControllerAdvice class
1
2
3
4
@RequestMapping(value = "somepath", method = RequestMethod.POST)
public GenericResponseDocument<?> someMethod(@RequestBody SomeObjectType requestDoc){
 ...
}

  • First method to override is the support method. This is where you can put code logic that tells this class whether to proceed with beforeBodyWrite method. You can always just return true, however in my example, I've just check again a list of valid controller method which I want to intercept.
  • Second method to override is the beforeBodyWrite method. This is where you'll implement the logic which you'll modify the ResponseBody. Remember that you'll need to return back the object type that you've modified. 
That's it. Feel free to post any questions below. I'll try to answer them as best as I could. Happy koding.

No comments:

Post a Comment