在 HttpServletRequest 上下文之外测试 Spring Boot WebClient

Posted

技术标签:

【中文标题】在 HttpServletRequest 上下文之外测试 Spring Boot WebClient【英文标题】:test a Spring Boot WebClient outside of a HttpServletRequest context 【发布时间】:2021-12-21 10:00:40 【问题描述】:

我的 Spring boot 2.5 应用程序是一个相当标准的 Web 应用程序,它公开一些端点并调用其他服务。 在通常的用例中,外部请求进来,并应用一些业务逻辑,这将调用其他服务,使用配置了 Oauth2 的 WebClient。

但是,我的应用程序还包含一个需要调用其他服务的计划任务。

这就是问题开始的地方:我相信在非 Servlet 环境中使用 webClient 时,我会遇到与 Spring Security 5 Calling OAuth2 Secured API in Application Runner results in IllegalArgumentException 相同的问题,即 servletRequest cannot be null

我认为我可以通过集成测试轻松重现这一点,但我不是。我已经有一个用于计划任务的SpringBootTest,在其中我加载了 Spring 上下文,访问具有逻辑的类,并简单地调用在 prod 中部署时计划的方法......但它没有失败:我无法复制。

当我调试测试时,我看到 servletRequest 在我运行测试时确实不为空(当它在 prod 中运行时):有一个 Spring MockhttpServletrequest,所以我没有与在生产中。

我尝试在完全没有“网络”支持的情况下运行测试,使用

@SpringBootTest(webEnvironment = WebEnvironment.NONE) 

但是当我这样做时,我的应用程序缺少一些必需的元素来启动:我在我的应用程序中配置了 2 个 webClients,一个是“正常”的,一个是我计划用于调度程序的:

  @Bean
  @Primary
  WebClient servletWebClient(ClientRegistrationRepository clientRegistrations,
                             OAuth2AuthorizedClientRepository authorizedClients) 

    //this constructor  will configure internally a RemoveAuthorizedClientOAuth2AuthorizationFailureHandler,
    // and onAuthorizationFailure will be called on it when we get a 401
    var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);

    oauth.setDefaultClientRegistrationId("keycloak");


    return WebClient.builder()
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .apply(oauth.oauth2Configuration())
        .build();
  

  @Bean
  @Qualifier("forScheduler")
  WebClient schedulerWebClient(OAuth2AuthorizedClientManager authorizedClientManager) 

    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);

    oauth.setDefaultClientRegistrationId("keycloak");

    return WebClient.builder()
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .apply(oauth.oauth2Configuration())
        .build();
  

  @Bean
  public OAuth2AuthorizedClientManager authorizedClientManagerForScheduler(
      ClientRegistrationRepository clientRegistrationRepository,
      OAuth2AuthorizedClientService clientService)
  

    OAuth2AuthorizedClientProvider authorizedClientProvider =
        OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build();

    AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
        new AuthorizedClientServiceOAuth2AuthorizedClientManager(
            clientRegistrationRepository, clientService);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
  

但是当使用 webEnvironment = WebEnvironment.NONE 配置我的测试时,没有 ClientRegistrationRepository 被加载,并且声明的 servletWebClient 不能被实例化,因为它是它的依赖项之一。

手动测试这个非常困难/昂贵,所以我真的很想通过我的集成测试获得某种验证,在我部署它之前,我对 2 个 webClients 的更改将起作用。

如何正确测试该场景?或者换句话说,有没有办法进行默认的 Spring Boot 测试,但没有 Spring 初始化 mockServletContext ?

【问题讨论】:

【参考方案1】:

我找到了另一种方法的解决方案,使用自定义 TestExecutionListener。

更多详情在https://***.com/a/69995757/3067542

【讨论】:

以上是关于在 HttpServletRequest 上下文之外测试 Spring Boot WebClient的主要内容,如果未能解决你的问题,请参考以下文章

Java源码分析系列之HttpServletRequest源码分析

JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(下)(各种乱码验证码重定向和转发)

java之spring mvc之数据处理

Request之登录系统跳转应用以及原理详解JavaWeb

JavaWeb_Servlet_02篇

JavaWeb_Servlet_02篇