Spring Reactive应用客户端报错及响应格式异常排查
Hey there! Let's break down why your WebClient isn't behaving like curl when connecting to your Spring Reactive streaming endpoint, and what that hex output you're seeing means.
First: What's That Hex Format?
The hex content you're seeing is a symptom of failed JSON parsing. Your server is sending streaming JSON (application/stream+json)—this is newline-delimited JSON (NDJSON), where each second a new JSON object is sent over a single long-lived connection. When your WebClient code tries to parse this continuous stream as a single Custom object (via bodyToMono), it can't handle the sequence of multiple JSON entries. The unparsed raw bytes end up being displayed as hex values instead of properly mapped Custom instances.
Why Your Current Code Isn't Working
Let's walk through the key issues in your client code:
while(true)is sending repeated unnecessary requests: This defeats the purpose of reactive streaming! Streaming endpoints are designed to keep one connection open and send data over time, not handle hundreds of rapid-fire requests. This overloads both client and server, leading to incomplete, messy responses.bodyToMono(Custom.class)is wrong for streaming:Monois built for single-value responses. For a continuous stream ofCustomobjects, you needFlux—Reactor's type for sequences/streaming data.- Missing error handling: You're not subscribing to the error signal, so you're missing critical details about why parsing is failing (which aligns with the error log you referenced).
Fixed WebClient Code
Here's the corrected approach to properly consume your streaming endpoint:
import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; import org.springframework.http.MediaType; public class StreamingClient { public static void main(String[] args) throws InterruptedException { WebClient webClient = WebClient.create("http://localhost:8080"); // Use Flux to handle the continuous streaming response Flux<Custom> customStream = webClient.get() .uri("/test") .accept(MediaType.APPLICATION_STREAM_JSON) .retrieve() .bodyToFlux(Custom.class); // Flux instead of Mono for streaming // Subscribe to the stream: handle each Custom object, errors, and completion customStream.subscribe( custom -> System.out.println("Received: " + custom), error -> System.err.println("Error streaming data: " + error.getMessage()), () -> System.out.println("Stream completed successfully") ); // Keep the main thread alive (Reactive code runs on daemon threads by default) Thread.currentThread().join(); } // Your Custom class (ensure it matches your server's JSON structure with proper getters/setters) static class Custom { private String someField; private int anotherField; // Add getters, setters, and a toString() method @Override public String toString() { return "Custom{someField='" + someField + "', anotherField=" + anotherField + "}"; } } }
Key Fixes Explained
- Switched from
MonotoFlux: This tells WebClient to expect a continuous sequence ofCustomobjects, which aligns perfectly with your server'sapplication/stream+jsonresponse type. - Removed the
while(true)loop: Now we make one single request that stays open, receiving data as it's sent by the server—this is how reactive streaming is intended to work. - Added error handling: The subscribe block now catches and logs errors, so you'll get clear feedback if there's still a parsing issue (like mismatched fields in your
Customclass). - Kept the main thread alive: Reactive code runs on daemon threads, so we need to prevent the main thread from exiting immediately to keep the client running.
Why Curl Works
Curl doesn't care about content type or structure—it just reads raw bytes from the connection and prints them to the terminal. It happily displays each newline-delimited JSON object as it arrives, without needing to parse or map them to a Java class. Your WebClient code failed because it was trying to force a streaming response into a single-object model.
内容的提问来源于stack exchange,提问作者Bilbo Baggins

