Using the Spring RestTemplate Interceptor – 使用Spring RestTemplate拦截器

最后修改: 2018年 5月 28日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to learn how to implement a Spring RestTemplate Interceptor.

在本教程中,我们将学习如何实现Spring RestTemplate 拦截器。

We’ll go through an example in which we’ll create an interceptor that adds a custom header to the response.

我们将通过一个例子,创建一个拦截器,在响应中添加一个自定义头。

2. Interceptor Usage Scenarios

2.拦截器的使用情况

Besides header modification, some of the other use-cases where a RestTemplate interceptor is useful are:

除了头的修改,RestTemplate拦截器有用的其他一些使用情况是。

  • Request and response logging
  • Retrying the requests with a configurable back off strategy
  • Request denial based on certain request parameters
  • Altering the request URL address

3. Creating the Interceptor

3.创建拦截器

In most programming paradigms, interceptors are an essential part that enables programmers to control the execution by intercepting it. Spring framework also supports a variety of interceptors for different purposes.

在大多数编程范式中,拦截器是一个必不可少的部分,它使程序员能够通过拦截来控制执行。Spring框架也支持各种不同用途的拦截器。

Spring RestTemplate allows us to add interceptors that implement ClientHttpRequestInterceptor interface. The intercept(HttpRequest, byte[], ClientHttpRequestExecution) method of this interface will intercept the given request and return the response by giving us access to the request, body and execution objects.

Spring RestTemplate允许我们添加实现ClientHttpRequestInterceptor接口的拦截器。这个接口的intercept(HttpRequest, byte[], ClientHttpRequestExecution)方法将拦截给定的请求,并通过让我们访问requestbodyexecution对象返回响应。

We’ll be using the ClientHttpRequestExecution argument to do the actual execution, and pass on the request to the subsequent process chain.

我们将使用ClientHttpRequestExecution参数来进行实际执行,并将请求传递给后续进程链。

As a first step, let’s create an interceptor class that implements the ClientHttpRequestInterceptor interface:

作为第一步,让我们创建一个拦截器类,实现ClientHttpRequestInterceptor接口:

public class RestTemplateHeaderModifierInterceptor
  implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(
      HttpRequest request, 
      byte[] body, 
      ClientHttpRequestExecution execution) throws IOException {
 
        ClientHttpResponse response = execution.execute(request, body);
        response.getHeaders().add("Foo", "bar");
        return response;
    }
}

Our interceptor will be invoked for every incoming request, and it will add a custom header Foo to every response, once the execution completes and returns.

我们的拦截器将对每个传入的请求进行调用,一旦执行完成并返回,它将在每个响应中添加一个自定义头Foo

Since the intercept() method included the request and body as arguments, it’s also possible to do any modification on the request or even denying the request execution based on certain conditions.

由于intercept()方法包括requestbody作为参数,所以也可以对请求做任何修改,甚至根据某些条件拒绝执行请求。

4. Setting up the RestTemplate

4.设置RestTemplate

Now that we have created our interceptor, let’s create the RestTemplate bean and add our interceptor to it:

现在我们已经创建了我们的拦截器,让我们创建RestTemplate bean并将我们的拦截器添加到其中。

@Configuration
public class RestClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        List<ClientHttpRequestInterceptor> interceptors
          = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
            interceptors = new ArrayList<>();
        }
        interceptors.add(new RestTemplateHeaderModifierInterceptor());
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }
}

In some cases, there might be interceptors already added to the RestTemplate object. So to make sure everything works as expected, our code will initialize the interceptor list only if it’s empty.

在某些情况下,可能已经有拦截器被添加到RestTemplate对象中。所以为了确保一切按预期进行,我们的代码将只在拦截器列表为空时才初始化它。

As our code shows, we are using the default constructor to create the RestTemplate object, but there are some scenarios where we need to read the request/response stream twice.

正如我们的代码所示,我们使用默认的构造函数来创建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. To cater such specific scenarios, Spring provides a special class called BufferingClientHttpRequestFactory. As the name suggests, this class will buffer the request/response in JVM memory for multiple usage.

默认实现允许我们只读取一次响应流。为了满足这样的特定场景,Spring提供了一个特殊的类,叫做BufferingClientHttpRequestFactory。顾名思义,这个类将在JVM内存中缓冲请求/响应,以便多次使用。

Here’s how the RestTemplate object is initialized using BufferingClientHttpRequestFactory to enable the request/response stream caching:

下面是如何使用BufferingClientHttpRequestFactory初始化RestTemplate对象以启用请求/响应流缓存的。

RestTemplate restTemplate 
  = new RestTemplate(
    new BufferingClientHttpRequestFactory(
      new SimpleClientHttpRequestFactory()
    )
  );

5. Testing Our Example

5.测试我们的例子

Here’s the JUnit test case for testing our RestTemplate interceptor:

下面是测试我们的RestTemplate拦截器的JUnit测试案例。

public class RestTemplateItegrationTest {
    
    @Autowired
    RestTemplate restTemplate;

    @Test
    public void givenRestTemplate_whenRequested_thenLogAndModifyResponse() {
        LoginForm loginForm = new LoginForm("username", "password");
        HttpEntity<LoginForm> requestEntity
          = new HttpEntity<LoginForm>(loginForm);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        ResponseEntity<String> responseEntity
          = restTemplate.postForEntity(
            "http://httpbin.org/post", requestEntity, String.class
          );
        
        Assertions.assertEquals(responseEntity.getStatusCode(), HttpStatus.OK);
        Assertions.assertEquals(responseEntity.getHeaders()
                .get("Foo")
                .get(0), "bar");
    }
}

Here, we’ve used the freely hosted HTTP request and response service http://httpbin.org to post our data. This testing service will return our request body along with some metadata.

在这里,我们使用了免费托管的HTTP请求和响应服务http://httpbin.org来发布我们的数据。这个测试服务将返回我们的请求体和一些元数据。

6. Conclusion

6.结论

This tutorial is all about how to set up an interceptor and add it to the RestTemplate object. This kind of interceptors can also be used for filtering, monitoring and controlling the incoming requests.

本教程主要介绍如何设置拦截器并将其添加到RestTemplate对象中。这种拦截器也可以用来过滤、监测和控制进入的请求。

A common use-case for a RestTemplate interceptor is the header modification – which we’ve illustrated in details in this article.

RestTemplate拦截器的一个常见用例是头的修改–我们在这篇文章中已经详细说明了这一点。

And, as always, you can find the example code over on Github project. This is a Maven-based project, so it should be easy to import and run as it is.

像往常一样,你可以在Github项目上找到示例代码。这是一个基于Maven的项目,所以它应该很容易导入和运行。