spring - 模拟第三方 API 时获取 "reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response"

标签 spring spring-boot reactive-programming mockmvc mockwebserver

我的 Spring boot API 正在使用 WebClient 使用第三方 API

DemoAPIController

@RestController
public class DemoAPIController
{
  @Autowired
  DemoService demoService;

  @GetMapping("/mytest")
  public Mono<UserType> getUserType()
  {
    return demoService.getUserType();
  }
}

DemoService.java

@Component
public class DemoService
{
  @Value("${base.url}")
  private String baseUrl;

  public Mono<UserType> getUserType()
  {
    System.out.println("baseUrl:" + baseUrl);
    WebClient webClient = WebClient.builder().baseUrl(baseUrl)
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .defaultHeader("apikey", "test").build();

    Mono<UserType> testResult = webClient.get()
        .uri("/xyz/xyz-api/v1/utility/users/ID873366777/usertype?appName=EAO").retrieve()
        .bodyToMono(UserType.class);
    testResult.subscribe(value -> System.out.println(value.getUserType()));
    return testResult;
  }
}

我使用 MockWebServer 模拟了第三方 API。当我尝试使用mockMvc.perform(...) 测试API 时。我的断言正在按预期发挥作用。但是在关闭 MockWebServer 时,我收到以下异常

2019-11-29 16:02:43.308  INFO 13048 --- [127.0.0.1:53970] okhttp3.mockwebserver.MockWebServer      : MockWebServer[443] received request: GET /valic/valic-security-data-api/v1/utility/users/ID873366777/usertype?appName=EAO HTTP/1.1 and responded: HTTP/1.1 200 OK
2019-11-29 16:03:18.362  INFO 13048 --- [ckWebServer 443] okhttp3.mockwebserver.MockWebServer      : MockWebServer[443] done accepting connections: socket closed
2019-11-29 16:03:18.385  WARN 13048 --- [ctor-http-nio-1] r.netty.http.client.HttpClientConnect    : [id: 0x186c45c1, L:0.0.0.0/0.0.0.0:53970 ! R:localhost/127.0.0.1:443] The connection observed an error

reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response

2019-11-29 16:03:18.389  INFO 13048 --- [127.0.0.1:53970] okhttp3.mockwebserver.MockWebServer      : MockWebServer[443] connection from /127.0.0.1 failed: java.net.SocketException: Socket closed
2019-11-29 16:03:20.509  INFO 13048 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

我的测试用例

@ExtendWith(SpringExtension.class)
@WebAppConfiguration()
@ContextConfiguration(classes = WebclientApplication.class)
@SpringBootTest
public class EmployeeServiceMockWebServerTest
{
  public static MockWebServer mockWebServer;
  private ObjectMapper MAPPER = new ObjectMapper();

  public MockMvc mockMvc;

  public MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
      MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));

  public MediaType contentTypeURLEncoded = new MediaType(MediaType.APPLICATION_FORM_URLENCODED.getType(),
      MediaType.APPLICATION_FORM_URLENCODED.getSubtype(), Charset.forName("utf8"));

  @Autowired
  private WebApplicationContext webApplicationContext;

  @BeforeAll
  static void setUp() throws IOException
  {
    mockWebServer = new MockWebServer();
    mockWebServer.start(443);
  }

  @AfterAll
  static void tearDown() throws IOException
  {
    mockWebServer.shutdown();
  }

  @BeforeEach
  void initialize()
  {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    System.out.println("Hostname:" + mockWebServer.getHostName());
    System.out.println("Port:" + mockWebServer.getPort());
  }

  @Test
  void getEmployeeById() throws Exception
  {
    UserType userType = new UserType();
    userType.setUserType("PAR-Mock");
    ServiceMessage serviceMessage = new ServiceMessage();
    serviceMessage.setCode("200");
    serviceMessage.setType("OK");
    serviceMessage.setDescription("From Mock");
    userType.setServiceMessage(serviceMessage);

    mockWebServer.enqueue(
        new MockResponse().setBody(MAPPER.writeValueAsString(userType)).addHeader("Content-Type", "application/json"));

    mockMvc.perform(get("/mytest").contentType(contentType)).andExpect(status().isOk());

    RecordedRequest recordedRequest = mockWebServer.takeRequest();
    assertEquals("GET", recordedRequest.getMethod());
    assertEquals("/logic/logic-security-data-api/v1/utility/users/ID873366777/usertype?appName=EAO",
        recordedRequest.getPath());
  }
}

我尝试了其他堆栈溢出帖子中提到的所有解决方案,但仍然遇到相同的错误。如有任何帮助,我们将不胜感激。

最佳答案

我通过评论以下行解决了该问题。

testResult.subscribe(value -> System.out.println(value.getUserType()));

我使用下面的代码创建了 WebClient 对象来获取额外的日志记录

WebClient webClient = WebClient.builder()
    .clientConnector(new ReactorClientHttpConnector(HttpClient.newConnection().compress(true).wiretap(true)))
    .baseUrl(baseUrl).defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .defaultHeader("apikey", "somekey").build();

我能够看到两个请求。然后在评论后,一切都按预期进行。

关于spring - 模拟第三方 API 时获取 "reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59111535/

相关文章:

angular - Rxjs - 使用 observables 作为值重新映射对象

javascript - 发布更改时防止 Meteor 删除已发送到客户端的 MiniMongo 数据

java - Spring:未调用自定义 validator

java - 使用 Gradle 构建的 Java Spring Boot 项目中偶尔出现 BeanCreationException

spring-boot - Spring Boot应用程序已弃用java.security.egd = file :/dev/./urandom吗?

java - 如何更新 TLS 版本

spring - 为什么 Spring 安全性基于用户名而不是用户 ID?

spring - 在 spring 集成中处理低级套接字错误的最佳实践?

java - Spring 的 AutowiredAnnotationBeanPostProcessor 和构造函数注入(inject)

java - Spring Boot 和 OAuth2 : Getting ResourceAccessException: I/O error on POST request for "http://localhost:5555/oauth/token": Connection refused: connect