I have a REST api that responds with some additional non JSON data in the body content. This breaks the use of RestTemplate and jackson. Can I intercept the http response body prior to the parsing?
I am using RestTemplate.getForObject.
I've taken a look at the RestTemplate and couldn't see an appropriate method.
To work with interceptor, you need to create @Component class that supports it and it should implement the HandlerInterceptor interface. preHandle() method − This is used to perform operations before sending the request to the controller. This method should return true to return the response to the client.
So, by simply using the RestTemplateBuilder our RestTemplate will automatically use a MappingJackson2HttpMessageConverter configured with an ObjectMapper that uses the required ParameterNamesModule.
Interceptors are generally used in Spring to intercept requests of either self created end point at Controller, OR, to intercept other(3rd party) api calls done by RestTemplate.
Even on the official Spring documentation, they advise to use WebClient instead. WebClient can basically do what RestTemplate does, making synchronous blocking calls. But it also has asynchronous capabilities, which makes it interesting. It has a functional way of programming, which makes it easy to read as well.
You can try to implement ClientHttpRequestInterceptor
and assign it to restTemplate
. Implement intercept
method:
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
ClientHttpResponse response=clientHttpRequestExecution.execute(httpRequest, bytes);
//...do magic with response body from getBody method
return response;
}
You might have to extend AbstractClientHttpResponse
with your own implementation to do that.
Another option could be to treat the response from the REST API as String, then format the string as needed and explicitly map it to object using ObjectMapper.
Then in your restTemplate
you would have:
String result = restTemplate.getForObject(url, String.class, host);
//..trim the extra stuff
MyClass object=objectMapper.readValue(result, MyClass.class);
Yet another option would be to implement your own HttpMessageConverter
which extends AbstractJackson2HttpMessageConverter
and register it with restTemplate
. In my opinion that would be the cleaneast from the Spring point of view
Another way would be to unwrap the response by implementing a ClientHttpRequestInterceptor along with a ClientHttpResponse.
@Component
public class MyInterceptor implements ClientHttpRequestInterceptor {
@Autowired
Function<ClientHttpResponse, MyResponseWrapper> responseWrapperBeanFactory;
@Autowired
MyRequestAdvice requestAdvice;
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
byte[] wrappedBody = requestAdvice.wrapRequest(bytes);
ClientHttpResponse res = clientHttpRequestExecution.execute(httpRequest, wrappedBody);
return responseWrapperBeanFactory.apply(res);
}
}
Here's the bean config for the MyResponseWrapper:
@Bean
Function<ClientHttpResponse, MyResponseWrapper> responseWrapperBeanFactory() {
return this::getMyResponseWrapper;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MyResponseWrapper getMyResponseWrapper(ClientHttpResponse originalResponse) {
return new MyResponseWrapper(originalResponse);
}
@Bean
public RestTemplate restTemplate(@Autowired MyInterceptor interceptor) {
RestTemplate t = new RestTemplate();
t.setInterceptors(Arrays.asList(interceptor));
// other setup code
return t;
}
And here's the ClientHttpResponse implementation:
public class MyResponseWrapper implements ClientHttpResponse {
private byte[] filteredContent;
private ByteArrayInputStream responseInputStream;
private ClientHttpResponse originalResponse;
public MyResponseWrapper(ClientHttpResponse originalResponse) {
this.originalResponse = originalResponse;
try {
filteredContent = MyContentUnwrapper.unwrapResponse(originalResponse.getBody().readAllBytes());
} catch (Exception e) {
throw new RuntimeException("There was a problem reading/decoding the response coming from the service ", e);
}
}
@Override
public HttpStatus getStatusCode() throws IOException {
return originalResponse.getStatusCode();
}
@Override
public int getRawStatusCode() throws IOException {
return originalResponse.getRawStatusCode();
}
@Override
public String getStatusText() throws IOException {
return originalResponse.getStatusText();
}
@Override
public void close() {
if (responseInputStream != null) {
try {
responseInputStream.close();
} catch (IOException e) { /* so long */}
}
}
@Override
public InputStream getBody() throws IOException {
if (responseInputStream == null) {
responseInputStream = new ByteArrayInputStream(filteredContent);
}
return responseInputStream;
}
@Override
public HttpHeaders getHeaders() {
return originalResponse.getHeaders();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With