Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the behaviour of a RestTemplate when multiple ClientHttpRequestInterceptors are registered?

Imagine I have the following the following 2 ClientHttpRequestInterceptors:

public class RequestLoggerInterceptor implements ClientHttpRequestInterceptor {
  @Override
  public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    log.info("HTTP request: {}", httpRequest);
    return clientHttpRequestExecution.execute(httpRequest, bytes);
  }
}
public class ResponseLoggerInterceptor implements ClientHttpRequestInterceptor {
  @Override
  public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);
    log.info("HTTP response: {}", response);
    return response
  }
}

And I add them both to the same RestTemplate:

ClientHttpRequestInterceptor requestLogger = new RequestLoggerInterceptor();
ClientHttpRequestInterceptor responseLoggerr = new ResponseLoggerInterceptor();
RestTemplate template = new RestTemplate();
template.setInterceptors(Arrays.asList(requestLogger, responseLogger));

What will be the behaviour when a request is executed using this RestTemplate?

In both ClientHttpRequestInterceptors, the clientHttpRequestExecution.execute method is called. Does this mean that the request is executed twice?

like image 654
Titulum Avatar asked Aug 21 '19 08:08

Titulum


People also ask

What is the use of ClientHttpRequestInterceptor?

Interface ClientHttpRequestInterceptor This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. Intercepts client-side HTTP requests.

How do you set the interceptor on a RestTemplate?

Setting up the RestTemplate For instance, if we want our interceptor to function as a request/response logger, then we need to read it twice – the first time by the interceptor and the second time by the client. The default implementation allows us to read the response stream only once.

How do you intercept all HTTP requests in spring boot?

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.

What is rest interceptor?

REST Interceptor to Log Requests and Responses Below given RequestResponseLoggingInterceptor class implements ClientHttpRequestInterceptor interface. It implements intercept() method. In this method, we are logging the request and response details sent from RestTemplate .


3 Answers

From the documentation of ClientHttpRequestExecution

Represents the context of a client-side HTTP request execution. Used to invoke the next interceptor in the interceptor chain, or - if the calling interceptor is last - execute the request itself.

Hence the request will not execute twice. It will execute in following way,

  1. start clientHttpRequestExecution.execute
  2. call first interceptor requestLogger.intercept
  3. call back clientHttpRequestExecution.execute
  4. call second interceptor responseLogger.intercept
  5. call back clientHttpRequestExecution.execute
  6. no interceptor left, call actual ClientHttpRequest.execute

For more details, read InterceptingClientHttpRequest InterceptingRequestExecution.execute method.

like image 109
samabcde Avatar answered Nov 02 '22 21:11

samabcde


RestTemplate allows you to register multiple HTTP request interceptors by implementing the ClientHttpRequestInterceptor(doc) interface. Every such interceptor is a pass through for the HTTP request, eventually executing the request after passing through all the interceptors.

The signature of the method to be implemented in the interceptor is

ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;

where the implementation of the execute method of ClientHttpRequestExecution(doc) interface takes care of passing the request to the next interceptor or executing the request if there are no more interceptors left to be intercepted by.

Since Spring 5.0.x, interceptors are sorted by Spring using AnnotationAwareOrderComparator(doc) and hence the sorting can be defined with @Order or @Priority too. So if you are in the said Spring version and you annotate the interceptors as below

@Order(1)
public class ResponseLoggerInterceptor implements ClientHttpRequestInterceptor {
...
}

@Order(2)
public class RequestLoggerInterceptor implements ClientHttpRequestInterceptor {
...
}

the request will first be intercepted by ResponseLoggerInterceptor and then by RequestLoggerInterceptor.

So in your case, the request will be intercepted by RequestLoggerInterceptor and ResponseLoggerInterceptor according to the sorted order and then finally executed. The request will be executed only once.

Note: RestTemplate will be deprecated in a future version and Spring suggests users to use WebClient instead of RestTemplate from version 5.0 onwards.

like image 30
Madhu Bhat Avatar answered Nov 02 '22 20:11

Madhu Bhat


No, the request is only executed once at the end. When you set the interceptors in the rest template the underlying ClientHttpRequestFactory is wrapped as InterceptingclientHttpRequestFactory with interceptors.

When you made the request using rest template the request is read by interceptors followed by request execution and the response is processed by interceptors. That is all done using ClientHttpRequestExecution.

like image 22
s7vr Avatar answered Nov 02 '22 20:11

s7vr