String encoding for any MIME type
CharSequenceEncoder now supports all MIME types, however since encoding Flux<String> can overlap with other encoders (e.g. SSE) there are now two ways to create a CharSequenceEncoder -- with support for text/plain only or with support for any MIME type. In WebFlux configuration we insert one CharSequenceEncoder for text/plain (as we have so far) and a second instance with support for any MIME type at the very end. Issue: SPR-15374
This commit is contained in:
@@ -37,7 +37,6 @@ import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
@@ -63,10 +62,18 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader<Objec
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with JSON {@code Encoder} for encoding objects.
|
||||
* Constructor without a {@code Decoder}. In this mode only {@code String}
|
||||
* is supported as the data of an event.
|
||||
*/
|
||||
public ServerSentEventHttpMessageReader() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with JSON {@code Decoder} for decoding to Objects. Support
|
||||
* for decoding to {@code String} event data is built-in.
|
||||
*/
|
||||
public ServerSentEventHttpMessageReader(Decoder<?> decoder) {
|
||||
Assert.notNull(decoder, "Decoder must not be null");
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
@@ -85,7 +92,7 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader<Objec
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType elementType, MediaType mediaType) {
|
||||
return MediaType.TEXT_EVENT_STREAM.isCompatibleWith(mediaType) ||
|
||||
return MediaType.TEXT_EVENT_STREAM.includes(mediaType) ||
|
||||
ServerSentEvent.class.isAssignableFrom(elementType.getRawClass());
|
||||
}
|
||||
|
||||
@@ -183,8 +190,6 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader<Objec
|
||||
public Mono<Object> readMono(ResolvableType elementType, ReactiveHttpInputMessage message,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
// For single String give StringDecoder a chance which comes after SSE in the order
|
||||
|
||||
if (String.class.equals(elementType.getRawClass())) {
|
||||
Flux<DataBuffer> body = message.getBody();
|
||||
return stringDecoder.decodeToMono(body, elementType, null, null).cast(Object.class);
|
||||
|
||||
@@ -27,6 +27,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
@@ -34,7 +35,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@code ServerHttpMessageWriter} for {@code "text/event-stream"} responses.
|
||||
@@ -53,18 +53,25 @@ public class ServerSentEventHttpMessageWriter implements ServerHttpMessageWriter
|
||||
private final Encoder<?> encoder;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor without an {@code Encoder}. In this mode only {@code String}
|
||||
* is supported for event data to be encoded.
|
||||
*/
|
||||
public ServerSentEventHttpMessageWriter() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with JSON {@code Encoder} for encoding objects. Support for
|
||||
* {@code String} event data is built-in.
|
||||
*/
|
||||
public ServerSentEventHttpMessageWriter(Encoder<?> encoder) {
|
||||
Assert.notNull(encoder, "'encoder' must not be null");
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured {@code Encoder}.
|
||||
* Return the configured {@code Encoder}, possibly {@code null}.
|
||||
*/
|
||||
public Encoder<?> getEncoder() {
|
||||
return this.encoder;
|
||||
@@ -78,7 +85,7 @@ public class ServerSentEventHttpMessageWriter implements ServerHttpMessageWriter
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType elementType, MediaType mediaType) {
|
||||
return mediaType == null || MediaType.TEXT_EVENT_STREAM.isCompatibleWith(mediaType) ||
|
||||
return mediaType == null || MediaType.TEXT_EVENT_STREAM.includes(mediaType) ||
|
||||
ServerSentEvent.class.isAssignableFrom(elementType.getRawClass());
|
||||
}
|
||||
|
||||
@@ -135,6 +142,10 @@ public class ServerSentEventHttpMessageWriter implements ServerHttpMessageWriter
|
||||
return Flux.from(encodeText(text.replaceAll("\\n", "\ndata:") + "\n", factory));
|
||||
}
|
||||
|
||||
if (this.encoder == null) {
|
||||
return Flux.error(new CodecException("No SSE encoder configured and the data is not String."));
|
||||
}
|
||||
|
||||
return ((Encoder<T>) this.encoder)
|
||||
.encode(Mono.just((T) data), factory, valueType, MediaType.TEXT_EVENT_STREAM, hints)
|
||||
.concatWith(encodeText("\n", factory));
|
||||
|
||||
Reference in New Issue
Block a user