Spring WebFlux WebClient返回200时出现UnsupportedOperationException异常排查
问题原因
你遇到的异常核心原因是Server-Sent Events(SSE)格式的响应只能被读取为Flux,而非Mono:
- 机场服务的验证接口通过
produces = MediaType.TEXT_EVENT_STREAM_VALUE声明输出SSE流式响应,Spring会使用ServerSentEventHttpMessageReader处理这种响应格式,而该阅读器明确不支持将SSE流读取为单个Mono对象。 - 尽管你的接口返回的是
Mono<Boolean>,但SSE格式要求响应是一个事件流——即使只有一个事件,客户端也必须以流的方式接收。而你在WebClient中调用bodyToMono(Boolean.class),强制要求将流转为单个对象,触发了不支持的操作异常。
解决方法
根据实际需求,有两种修复方案:
方案1:改用普通JSON响应(推荐,你的场景不需要流式输出)
修改机场服务控制器
把接口的produces改为普通JSON格式,或直接去掉该属性(Spring WebFlux默认输出JSON):
@RestController @RequestMapping("/v1/airports") @AllArgsConstructor public class AirportsController { private static final List<String> validCodes = List.of("MLG","ROM"); @GetMapping(value = "/validate/{airportCode}") // 移除produces,或改为produces = MediaType.APPLICATION_JSON_VALUE @Operation(description = "Indicates if the airport code is valid or not") @ResponseStatus(HttpStatus.OK) public Mono<Boolean> validateAirportCode(@PathVariable String airportCode){ if(validCodes.contains( airportCode )){ return Mono.just( true ).log(); }else { return Mono.just( false ).log(); } } }
修改WebClient客户端
去掉指定TEXT_EVENT_STREAM_VALUE的accept设置,让客户端接收JSON响应:
@Component @AllArgsConstructor public class AirportsWebClient { public Mono<Boolean> validateAirportCode(String airportCode){ WebClient airportsWebClient = WebClient.builder().baseUrl( "http://localhost:8082" ).build(); return airportsWebClient.get() .uri( "/v1/airports/validate/" + airportCode ) // 移除accept(TEXT_EVENT_STREAM_VALUE),或改为accept(MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono( Boolean.class ); } }
方案2:保留SSE格式,调整客户端接收方式
如果确实需要使用SSE格式(比如后续要扩展为流式输出),则客户端需要将响应读取为Flux,再通过next()获取第一个事件转为Mono:
@Component @AllArgsConstructor public class AirportsWebClient { public Mono<Boolean> validateAirportCode(String airportCode){ WebClient airportsWebClient = WebClient.builder().baseUrl( "http://localhost:8082" ).build(); return airportsWebClient.get() .uri( "/v1/airports/validate/" + airportCode ) .accept( MediaType.valueOf( MediaType.TEXT_EVENT_STREAM_VALUE ) ) .retrieve() .bodyToFlux( Boolean.class ) .next(); // 从流中获取第一个元素转为Mono } }
内容的提问来源于stack exchange,提问作者Gabriel García Garrido
相关产品推荐
相关产品推荐

