I wanted to modify a REST service with a change in the response.
The service was already doing what it was intended to do . There was a requirement to change certain things related to the representation of the response.
I didn’t want to change the existing code and clutter it with this logic.
So I used an advice instead.
Spring Boot provides RestControllerAdvice to handle this logic.
Here are the steps to do it:
STEP 1:
Create a RestControllerAdvice class (annotate a class with @RestControllerAdvice ) and implement ResponseBodyAdvice interface:
package com.springboot.restadvice; import java.util.Map; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; @RestControllerAdvice public class RestAdvice implements ResponseBodyAdvice<T> { }
STEP 2:
Implement the method required by the interface ResponseBodyAdvice (Replace <T> parameter in the interface with the return type of the REST service ):
package com.springboot.restadvice; import java.util.Map; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; @RestControllerAdvice public class RestAdvice implements ResponseBodyAdvice<Map<String, String>> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { String className = returnType.getContainingClass().toString(); String methodName = returnType.getMethod().toString(); if (className.contains("RestController") && methodName.contains("modifyRestResponse")) { return true; } return false; } @Override public Map<String, String> beforeBodyWrite(Map<String, String> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { body.put("message", "Hi! This is Response Body Advice modifying the response"); return body; } }
There are two methods:
- supports()
- beforeBodyWrite()
the supports() method decides when to apply the advice.
In the above case I have checked for the class and the method for which I want to apply this advice.
the beforeBodyWrite() method lets you to modify the response.
In the above case I have put a message on the response .
That is all the change we need to do!
Here is the REST controller for which I am applying this advice:
package com.springboot.restadvice; import java.util.HashMap; import java.util.Map; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @org.springframework.web.bind.annotation.RestController public class RestController { @PostMapping("/modifyRESTResponse") public Map<String, String> modifyRestResponse(@RequestBody Map<String,String> message) { Map<String, String> result = new HashMap<>(); result.put("name", "Mr Spring Boot Advice"); result.putAll(message); return result; } }
As you see , the above rest service just returns the message posted by the user along with the name “Mr Spring Boot Advice”.
Here is the response what I got before applying the advice:

And below is the response after applying the advice:

The response has been modified by the advice!
I have not touched the existing controller class.
This provides loose coupling for the logic I added. Also the same advice can be applied to multiple REST services.
Here is the code :