Spring RestTemplate进阶:拦截器

2023-03-07 16:33:14 浏览数 (1)

原文链接:https://www.baeldung.com/spring-rest-template-interceptor

作者: baeldung

译者:helloworldtang

1. 概览

在这篇文章中,我们将学习如何实现一个Spring RestTemplate 拦截器。

文中将通过一个示例来展示如何创建一个Spring RestTemplate拦截器及如何使用这个拦截器来添加一个自定义HTTP头。

2. 拦截器的使用场景

除了修改HTTP头之外,RestTemplate拦截器还可以用于下面的场景:

  • 打印请求和响应日志
  • 用可配置的回滚策略进行重试
  • 基于某些请求参数来拒绝请求
  • 改变请求的URL

3. 创建拦截器

在大多数编程范例中,拦截器是程序员能够通过拦截来控制程序执行的重要途径。基于不同的场景,Spring框架还支持各式各样的拦截器。

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

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

首先,让我们创建一个实现了ClientHttpRequestInterceptor接口的拦截器类

代码语言:javascript复制
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;
    }
}

我们的拦截器将被用于每个传入的请求,并且一旦执行完成,在返回前,这个拦截器将向每个响应添加一个自定义的HTTP头Foo

由于intercept()方法的参数签名包含requestbody,所以还可以根据某些条件对请求进行任何修改,甚至拒绝请求执行。

4. 配置RestTemplate

既然已经创建了拦截器,那么就让我们在创建RestTemplate bean时添加这个拦截器:

代码语言:javascript复制
@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;
    }
}

在某些情况下,RestTemplate对象可能已经添加了拦截器。因此,为了确保一切正常工作,上面的示例代码只在拦截器列表为空的时候才重新进行初始化。

正如上面的代码所示,我们使用默认的构造函数来创建RestTemplate对象,但在某些情况下,我们需要读取请求/响应流两次。

例如,如果我们希望拦截器用作请求/响应记录器,那么就需要读取两次——第一次由拦截器读取,第二次由客户端读取。

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

下面介绍如何使用BufferingClientHttpRequestFactory来初始化RestTemplate并启用请求/响应流缓存:

代码语言:javascript复制
RestTemplate restTemplate 
  = new RestTemplate(
    new BufferingClientHttpRequestFactory(
      new SimpleClientHttpRequestFactory()
    )
  );

5. 测试

下面是测试RestTemplate拦截器的JUnit测试用例:

代码语言:javascript复制
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
          );

        assertThat(
          responseEntity.getStatusCode(),
          is(equalTo(HttpStatus.OK))
        );
        assertThat(
          responseEntity.getHeaders().get("Foo").get(0),
          is(equalTo("bar"))
        );
    }
}

在上面的例子中,我们将请求数据发送到一个免费托管的HTTP服务器http://httpbin.org。这个测试服务器将返回我们的请求体以及一些元数据。

6. 总结

本教程介绍如何设置拦截器并将其配置到RestTemplate对象中。这种拦截器还可以用于过滤、监控和控制传入的请求。 RestTemplate拦截器的一个常用场景是修改HTTP头——我们已经在本文中详细说明了这一点。

和往常一样,文中用到的示例代码可以在Github项目上找到。这是一个基于maven的项目,因此应该很容易导入和运行。

0 人点赞