You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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模拟外部服务的返回,不需要修改业务逻辑即可完成全链路测试:

  1. 首先引入测试依赖(Maven/Gradle按需引入对应版本的com.squareup.okhttp3:mockwebserver依赖即可)
  2. 测试代码示例:
@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 07:09:04