Spring Webflux:WebClient调用返回201无响应体时如何返回200带体及组件测试
201状态码转200带响应体的最佳实践
你当前用bodyToMono(Void.class)处理无响应体的201返回是正确的,只需要在调用完成后追加自定义响应构造逻辑即可,注意尽量不要使用block()方法阻塞响应式线程,推荐直接返回响应式包装类对接上层接口:
- 如果可以接受所有2xx状态码都视为调用成功,直接在
bodyToMono(Void.class)后拼接构造逻辑:
public Mono<ResponseEntity<CustomResponse>> createEmployee(Employee employee) { return webClient.post() .uri("/employee") .bodyValue(employee) .retrieve() .onStatus(status -> status.equals(HttpStatus.NOT_FOUND), response -> Mono.just(new NotFoundException("404 occurred", HttpStatus.NOT_FOUND.name()))) .bodyToMono(Void.class) // 调用成功后构造自定义响应 .then(Mono.just(new CustomResponse("员工创建成功", LocalDateTime.now()))) .map(ResponseEntity::ok); }
- 如果需要严格校验外部服务必须返回201才视为成功,建议改用
exchangeToMono显式判断状态码,避免其他2xx状态码误判:
public Mono<ResponseEntity<CustomResponse>> createEmployee(Employee employee) { return webClient.post() .uri("/employee") .bodyValue(employee) .exchangeToMono(response -> { if (HttpStatus.CREATED.equals(response.statusCode())) { return response.bodyToMono(Void.class) .then(Mono.just(ResponseEntity.ok(new CustomResponse("员工创建成功", LocalDateTime.now())))); } else if (HttpStatus.NOT_FOUND.equals(response.statusCode())) { return Mono.error(new NotFoundException("404 occurred", HttpStatus.NOT_FOUND.name())); } return response.createException().flatMap(Mono::error); }); }
如果是Servlet环境必须同步调用,只需要在最终返回的Mono后调用block()获取ResponseEntity对象返回即可。
201返回场景的组件测试编写
推荐使用OkHttp提供的MockWebServer模拟外部服务的返回,不需要修改业务逻辑即可完成全链路测试:
- 首先引入测试依赖(Maven/Gradle按需引入对应版本的
com.squareup.okhttp3:mockwebserver依赖即可) - 测试代码示例:
@SpringBootTest @AutoConfigureWebTestClient class EmployeeApiTest { private MockWebServer mockExternalServer; @Autowired private WebTestClient webTestClient; @Autowired private EmployeeService employeeService; @BeforeEach void init() throws IOException { // 启动模拟外部服务 mockExternalServer = new MockWebServer(); mockExternalServer.start(); // 将业务代码里WebClient的调用地址替换为模拟服务地址 employeeService.setWebClientBaseUrl(mockExternalServer.url("/").toString()); } @AfterEach void close() throws IOException { mockExternalServer.shutdown(); } @Test void should_return_200_with_custom_body_when_external_server_return_201() { // 模拟外部服务返回201无响应体 mockExternalServer.enqueue(new MockResponse() .setResponseCode(HttpStatus.CREATED.value()) .setBody("")); // 调用本服务的接口并断言结果 webTestClient.post() .uri("/your-api-endpoint") .bodyValue(new Employee("张三", "研发部")) .exchange() .expectStatus().isOk() .expectBody(CustomResponse.class) .consumeWith(res -> { CustomResponse body = res.getResponseBody(); assert body != null; assert "员工创建成功".equals(body.getMessage()); }); } @Test void should_throw_not_found_exception_when_external_server_return_404() { // 模拟外部服务返回404 mockExternalServer.enqueue(new MockResponse() .setResponseCode(HttpStatus.NOT_FOUND.value())); webTestClient.post() .uri("/your-api-endpoint") .bodyValue(new Employee("张三", "研发部")) .exchange() .expectStatus().isNotFound(); } }
如果是Servlet架构的Spring Boot项目,把WebTestClient替换为MockMvc即可,模拟外部服务的逻辑完全一致。
内容的提问来源于stack exchange,提问作者Spartacus
相关产品推荐
相关产品推荐

