Spring Boot微服务间WebClient通信:接收方未获取到请求体
我有两个注册在Eureka上的Spring Boot微服务,尝试通过HTTP建立通信,使用WebClient从服务A向服务B发送POST请求,代码如下:
WebClient webClient = webClientBuilder.build(); System.out.println(request.getAuthId()); System.out.println(request.getFirstName()); Map<String, String> valueMap = new HashMap<>(); valueMap.put("firstName", request.getFirstName()); valueMap.put("lastName", request.getLastName()); valueMap.put("authId", request.getAuthId()); Mono<MessageResponse> createdUser = webClient.post() .uri("http://market-service/market/agrinet/auth/add-user") .header("language", locale.getLanguage()) .header("accept-language",locale.getLanguage().toUpperCase()) .headers(h -> h.setBearerAuth(jwt.getTokenValue())) .contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(valueMap)) .retrieve() .bodyToMono(MessageResponse.class); return createdUser.block();
我参考其他方案尝试了Map传参方式,也试过直接通过.bodyValue()发送request对象,但两种方式都无效:请求到达服务B时请求体丢失,不过请求头是正常存在的。我查阅了大量资料,但这是个很标准的场景,却找不到问题所在,请问我遗漏了什么?
排查与解决方向
检查服务B的请求接收注解:服务B必须使用
@RequestBody注解来接收JSON格式的请求体,不能用@RequestParam或无注解的参数。示例代码:// 用Map接收 @PostMapping("/market/agrinet/auth/add-user") public ResponseEntity<MessageResponse> addUser( @RequestBody Map<String, String> requestBody, @RequestHeader("language") String language ) { // 业务逻辑 return ResponseEntity.ok(new MessageResponse()); } // 或用自定义DTO接收(需确保DTO有getter/setter和无参构造器) @PostMapping("/market/agrinet/auth/add-user") public ResponseEntity<MessageResponse> addUser( @RequestBody UserRequest userRequest, @RequestHeader("language") String language ) { // 业务逻辑 return ResponseEntity.ok(new MessageResponse()); }验证请求对象的序列化能力:如果直接发送自定义request对象,要确保该对象能被Jackson正常序列化。可以在服务A中先序列化测试:
ObjectMapper objectMapper = new ObjectMapper(); try { String jsonBody = objectMapper.writeValueAsString(request); System.out.println("序列化后的请求体: " + jsonBody); } catch (JsonProcessingException e) { e.printStackTrace(); // 这里会暴露序列化失败的原因,比如缺少无参构造器、字段无getter }排查链路中的拦截器/过滤器:检查服务A、服务B或中间网关是否存在自定义的WebFilter、ClientHttpRequestInterceptor,这类组件如果读取了请求体但未重新写入,会导致下游服务无法获取请求体。比如某些日志过滤器会消费掉DataBuffer,需要确保处理后重新构建请求体。
确认服务发现与请求寻址的正确性:虽然请求头能到达,但可以验证
market-service是否正确指向服务B实例。可以通过Eureka控制台查看服务状态,或者在服务A中通过DiscoveryClient获取实例地址后替换硬编码URI测试:List<ServiceInstance> instances = discoveryClient.getInstances("market-service"); if (!instances.isEmpty()) { String targetUri = instances.get(0).getUri() + "/market/agrinet/auth/add-user"; // 使用targetUri构造WebClient请求 }添加请求日志排查发送端问题:在服务A的WebClient中添加过滤器,打印实际发送的请求体,确认请求体是否正确发出:
WebClient webClient = webClientBuilder .filter((clientRequest, exchangeFunction) -> { clientRequest.body().subscribe(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); System.out.println("实际发送的请求体: " + new String(bytes, StandardCharsets.UTF_8)); // 读取后需释放资源 DataBufferUtils.release(dataBuffer); }); return exchangeFunction.exchange(clientRequest); }) .build();如果这里能打印出正确的请求体,说明问题出在服务B的接收环节或中间链路。
内容的提问来源于stack exchange,提问作者Dusan Madzarevic

