简体   繁体   中英

RestTemplate Interceptor

I'm currently trying to incorporate a HandlerInterceptorAdapter but it's not getting registered and comparing it to other answers is tough because everyone is using something different. And I'm aware WebMvcConfigureAdapter is deprecated, some versioning is beyond my control for the scope of the project, see usage specs below.

Can someone please provide some guidance on incorporating interceptors with a RestTemplate (that's not ClientHttpRequestInterceptor).

Main:

@SpringBootApplication
@EnableRetry
public class Application extends SpringBootServletInitializer {

  public static void main(String[] args) {

   ApplicationContext ctx = SpringApplication.run(Application.class, args);

  }


  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder applicationBuilder) {

    return applicationBuilder.sources(Application.class);

  }

  @Bean
  private RestTemplate restTemplate(){
    Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("redacted", 8080));

    SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
    simpleClientHttpRequestFactory.setProxy(proxy);
    simpleClientHttpRequestFactory.setOutputStreaming(false);

    RestTemplate template = new RestTemplate();
    template.setErrorHandler(new MyResponseErrorHandler());

    return template;
  }
}

Interceptor : com.example.foo.config.request.interceptor

@Component
public class MyInterceptor extends HandlerInterceptorAdapter {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("INTERCEPTED");
    return super.preHandle(request, response, handler);
  }
}

InterceptorConfig : com.example.foo.config.request.interceptor

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter  {

  @Bean
  MyInterceptor myInterceptor() {
    return new MyInterceptor();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    super.addInterceptors(registry);
    System.out.println("Adding interceptor");
    registry.addInterceptor(myInterceptor());
  }

}

"Adding interceptor" does get logged so I know the configs are being scanned. I just can't get any interceptor logic to log.

Using:

  • Spring Boot v1.5.15
  • Spring Version: 4.3.18.RELEASE

HandlerInterceptorAdapter is an implementation that applies to @Controller or @RestController . Not an implementation for RestTemplete .

To apply it to RestTemplete , you need to use ClientHttpRequestInterceptor .

ex.

@Component
public class CustomInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        // ... 
    }
}
@Configuation
public class RestTempleteConfig {

    // ...
    @Autowired
    private CustomInterceptor customInterceptor;

    @Bean
    public RestTemplate restTemplate(){
        RestTemplate template = new RestTemplate();
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        template.add(customInterceptor);
        return template;
    }
}

RestTemplate expects ClientHttpRequestInterceptor

 setInterceptors(List<ClientHttpRequestInterceptor> interceptors) 

Set the request interceptors that this accessor should use.

You can use Servlet Filter to "intercept" requests/response,

 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; 

implement this with a servlet filter. No Spring involved here at all

But you will have to change RestTemplate to using other framework as jersey

Jersey gives a very handy implementation of such as filter called LoggingFilter which can help in logging all kinds of incoming and outgoing traffic.

As @WonChulHeo noted you can't use HandlerInterceptorAdapter with RestTemplate . Only ClientHttpRequestInterceptor . It's not clear why do you need exactly HandlerInterceptorAdapter - we can only see that you are trying to log the fact of the request interception. And ClientHttpRequestInterceptor is absolutely able to do the same and even more - check my working example bellow.

PS There is an error in your code - you can't use private access for @Bean methods - check your private RestTemplate restTemplate() { please...

@Slf4j
@RestController
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class)
                .bannerMode(Banner.Mode.OFF)
                .run(args);
    }

    @GetMapping("/users/{id}")
    public User get(@PathVariable int id) {
        log.info("[i] Controller: received request GET /users/{}", id);
        return new User(id, "John Smith");
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder templateBuilder) {
        ClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
        return templateBuilder
                .interceptors((request, bytes, execution) -> {
                    URI uri = request.getURI();
                    HttpMethod method = request.getMethod();

                    log.info("[i] Interceptor: requested {} {}", method, uri);
                    log.info("[i] Interceptor: request headers {}", request.getHeaders());

                    ClientHttpRequest delegate = requestFactory.createRequest(uri, method);
                    request.getHeaders().forEach((header, values) -> delegate.getHeaders().put(header, values));

                    ClientHttpResponse response = delegate.execute();
                    log.info("[i] Interceptor: response status: {}", response.getStatusCode().name());
                    log.info("[i] Interceptor: response headers: {}", response.getHeaders());
                    String body = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
                    log.info("[i] Interceptor: response body: '{}'", body);

                    return response;
                })
                .rootUri("http://localhost:8080")
                .build();
    }

    @Bean
    ApplicationRunner run(RestTemplate restTemplate) {
        return args -> {
            ResponseEntity<User> response = restTemplate.getForEntity("/users/{id}", User.class, 1);
            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("[i] User: {}", response.getBody());
            } else {
                log.error("[!] Error: {}", response.getStatusCode());
            }
        };
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
}

The HandlerInterceptorAdapter is for the server side (ie RestController) to intercept some important events when server processes a HTTP request , it is nothing to do with what HTTP client (eg RestTemplate ) is used.

If you want to use RestTemplate as a HTTP client and want to intercept the request just before it sent out and the response just after it received , you must use ClientHttpRequestInterceptor .

I'm trying to intercept requests and responses in a more flexible way than ClientHttpRequestInterceptor.

From your comment above , what is your actual use-cases that it cannot handle ? I think ClientHttpRequestInterceptor is already quite flexible enough to implement any complex logic to intercept request and response. As your question does not provide any information about how you need to intercept , I can only give a general example to show what the ClientHttpRequestInterceptor can offer .

To configure the RestTemplate to use an interceptor :

RestTemplate rt = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors= new ArrayList<ClientHttpRequestInterceptor>();
inteceptors.add(new MyClientHttpRequestInterceptor());

And the ClientHttpRequestInterceptor looks like:

public class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor{

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
            throws IOException {

        //The HTTP request and its body are intercepted here which you can log them or modify them. e.g.
        System.out.println("Log the HTTP request header: " + request.getHeaders());

        //Modify the HTTP request header....
        request.getHeaders().add("foo", "fooValue");

        //Throw exception if you do not want to send the HTTP request

        //If it is at the end of the interceptor chain , call execution.execute() to confirm sending the HTTP request will return the response in ClientHttpResponse
        //Otherwise, it will pass the request to the next interceptor in the chain to process
        ClientHttpResponse response= execution.execute(request, body);

        //The HTTP response is intercepted here which you can log them or modify them.e.g.
        System.out.println("Log the HTTP response header: " + response.getHeaders());

        //Modify the HTTP response header
        response.getHeaders().add("bar", "barValue");

        return response;
    }
}

Please note that you can also configure a chain of ClientHttpRequestInterceptor which allows to split some complex request and response intercept logic into many smalls and reusable ClientHttpRequestInterceptor . It is designed with the Chain of responsibility design pattern which its API experience is very similar to what Filter#doFilter() in Servlet .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM