Allow to pass hints parameter in HttpMessageReader/Writer
Issue: SPR-14557
This commit is contained in:
@@ -18,6 +18,7 @@ package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -55,8 +56,8 @@ public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType type, MediaType mediaType) {
|
||||
return this.decoder != null && this.decoder.canDecode(type, mediaType);
|
||||
public boolean canRead(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
|
||||
return this.decoder != null && this.decoder.canDecode(type, mediaType, hints);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,21 +67,21 @@ public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
|
||||
|
||||
|
||||
@Override
|
||||
public Flux<T> read(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
public Flux<T> read(ResolvableType type, ReactiveHttpInputMessage inputMessage, Map<String, Object> hints) {
|
||||
if (this.decoder == null) {
|
||||
return Flux.error(new IllegalStateException("No decoder set"));
|
||||
}
|
||||
MediaType contentType = getContentType(inputMessage);
|
||||
return this.decoder.decode(inputMessage.getBody(), type, contentType);
|
||||
return this.decoder.decode(inputMessage.getBody(), type, contentType, hints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
public Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage, Map<String, Object> hints) {
|
||||
if (this.decoder == null) {
|
||||
return Mono.error(new IllegalStateException("No decoder set"));
|
||||
}
|
||||
MediaType contentType = getContentType(inputMessage);
|
||||
return this.decoder.decodeToMono(inputMessage.getBody(), type, contentType);
|
||||
return this.decoder.decodeToMono(inputMessage.getBody(), type, contentType, hints);
|
||||
}
|
||||
|
||||
private MediaType getContentType(ReactiveHttpInputMessage inputMessage) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -59,8 +60,8 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType) {
|
||||
return this.encoder != null && this.encoder.canEncode(type, mediaType);
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
|
||||
return this.encoder != null && this.encoder.canEncode(type, mediaType, hints);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,7 +72,8 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
if (this.encoder == null) {
|
||||
return Mono.error(new IllegalStateException("No decoder set"));
|
||||
@@ -99,7 +101,7 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
}
|
||||
|
||||
DataBufferFactory bufferFactory = outputMessage.bufferFactory();
|
||||
Flux<DataBuffer> body = this.encoder.encode(inputStream, bufferFactory, type, contentType);
|
||||
Flux<DataBuffer> body = this.encoder.encode(inputStream, bufferFactory, type, contentType, hints);
|
||||
return outputMessage.writeWith(body);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -31,6 +33,7 @@ import org.springframework.http.ReactiveHttpInputMessage;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface HttpMessageReader<T> {
|
||||
@@ -40,15 +43,10 @@ public interface HttpMessageReader<T> {
|
||||
* @param type the type to test for readability
|
||||
* @param mediaType the media type to read, can be {@code null} if not specified.
|
||||
* Typically the value of a {@code Content-Type} header.
|
||||
* @param hints additional information about how to do read
|
||||
* @return {@code true} if readable; {@code false} otherwise
|
||||
*/
|
||||
boolean canRead(ResolvableType type, MediaType mediaType);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects that can be read by this converter.
|
||||
* @return the list of supported readable media types
|
||||
*/
|
||||
List<MediaType> getReadableMediaTypes();
|
||||
boolean canRead(ResolvableType type, MediaType mediaType, Map<String, Object> hints);
|
||||
|
||||
/**
|
||||
* Read a {@link Flux} of the given type form the given input message, and returns it.
|
||||
@@ -56,9 +54,10 @@ public interface HttpMessageReader<T> {
|
||||
* passed to the {@link #canRead canRead} method of this interface, which must have
|
||||
* returned {@code true}.
|
||||
* @param inputMessage the HTTP input message to read from
|
||||
* @param hints additional information about how to do read
|
||||
* @return the converted {@link Flux} of elements
|
||||
*/
|
||||
Flux<T> read(ResolvableType type, ReactiveHttpInputMessage inputMessage);
|
||||
Flux<T> read(ResolvableType type, ReactiveHttpInputMessage inputMessage, Map<String, Object> hints);
|
||||
|
||||
/**
|
||||
* Read a {@link Mono} of the given type form the given input message, and returns it.
|
||||
@@ -66,8 +65,15 @@ public interface HttpMessageReader<T> {
|
||||
* passed to the {@link #canRead canRead} method of this interface, which must have
|
||||
* returned {@code true}.
|
||||
* @param inputMessage the HTTP input message to read from
|
||||
* @param hints additional information about how to do read
|
||||
* @return the converted {@link Mono} of object
|
||||
*/
|
||||
Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage);
|
||||
Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage, Map<String, Object> hints);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects that can be read by this converter.
|
||||
* @return the list of supported readable media types
|
||||
*/
|
||||
List<MediaType> getReadableMediaTypes();
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -31,6 +33,7 @@ import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface HttpMessageWriter<T> {
|
||||
@@ -40,15 +43,10 @@ public interface HttpMessageWriter<T> {
|
||||
* @param type the class to test for writability
|
||||
* @param mediaType the media type to write, can be {@code null} if not specified.
|
||||
* Typically the value of an {@code Accept} header.
|
||||
* @param hints additional information about how to do write
|
||||
* @return {@code true} if writable; {@code false} otherwise
|
||||
*/
|
||||
boolean canWrite(ResolvableType type, MediaType mediaType);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects that can be written by this converter.
|
||||
* @return the list of supported readable media types
|
||||
*/
|
||||
List<MediaType> getWritableMediaTypes();
|
||||
boolean canWrite(ResolvableType type, MediaType mediaType, Map<String, Object> hints);
|
||||
|
||||
/**
|
||||
* Write an given object to the given output message.
|
||||
@@ -57,9 +55,16 @@ public interface HttpMessageWriter<T> {
|
||||
* @param contentType the content type to use when writing. May be {@code null} to
|
||||
* indicate that the default content type of the converter must be used.
|
||||
* @param outputMessage the message to write to
|
||||
* @param hints additional information about how to do write
|
||||
* @return the converted {@link Mono} of object
|
||||
*/
|
||||
Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage);
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects that can be written by this converter.
|
||||
* @return the list of supported readable media types
|
||||
*/
|
||||
List<MediaType> getWritableMediaTypes();
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.http.codec;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -59,14 +60,14 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<? extends Resource> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints) {
|
||||
|
||||
return Mono.from(Flux.from(inputStream).
|
||||
take(1).
|
||||
concatMap(resource -> {
|
||||
HttpHeaders headers = outputMessage.getHeaders();
|
||||
addHeaders(headers, resource, contentType);
|
||||
return writeContent(resource, type, outputMessage);
|
||||
return writeContent(resource, type, outputMessage, hints);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Void> writeContent(Resource resource, ResolvableType type, ReactiveHttpOutputMessage outputMessage) {
|
||||
private Mono<Void> writeContent(Resource resource, ResolvableType type, ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints) {
|
||||
if (outputMessage instanceof ZeroCopyHttpOutputMessage) {
|
||||
Optional<File> file = getFile(resource);
|
||||
if (file.isPresent()) {
|
||||
@@ -97,7 +98,7 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
|
||||
|
||||
// non-zero copy fallback, using ResourceEncoder
|
||||
return super.write(Mono.just(resource), type,
|
||||
outputMessage.getHeaders().getContentType(), outputMessage);
|
||||
outputMessage.getHeaders().getContentType(), outputMessage, hints);
|
||||
}
|
||||
|
||||
private static Optional<Long> contentLength(Resource resource) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.http.codec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -61,7 +62,7 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType) {
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
|
||||
return mediaType == null || TEXT_EVENT_STREAM.isCompatibleWith(mediaType);
|
||||
}
|
||||
|
||||
@@ -71,19 +72,19 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<?> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
|
||||
public Mono<Void> write(Publisher<?> inputStream, ResolvableType type, MediaType contentType,
|
||||
ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints) {
|
||||
|
||||
outputMessage.getHeaders().setContentType(TEXT_EVENT_STREAM);
|
||||
|
||||
DataBufferFactory bufferFactory = outputMessage.bufferFactory();
|
||||
Flux<Publisher<DataBuffer>> body = encode(inputStream, bufferFactory, type);
|
||||
Flux<Publisher<DataBuffer>> body = encode(inputStream, bufferFactory, type, hints);
|
||||
|
||||
return outputMessage.writeAndFlushWith(body);
|
||||
}
|
||||
|
||||
private Flux<Publisher<DataBuffer>> encode(Publisher<?> inputStream,
|
||||
DataBufferFactory bufferFactory, ResolvableType type) {
|
||||
private Flux<Publisher<DataBuffer>> encode(Publisher<?> inputStream, DataBufferFactory bufferFactory,
|
||||
ResolvableType type, Map<String, Object> hints) {
|
||||
|
||||
return Flux.from(inputStream)
|
||||
.map(o -> toSseEvent(o, type))
|
||||
@@ -105,7 +106,7 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
return Flux.empty();
|
||||
}
|
||||
else {
|
||||
return applyEncoder(data, bufferFactory);
|
||||
return applyEncoder(data, bufferFactory, hints);
|
||||
}
|
||||
}).orElse(Flux.empty());
|
||||
|
||||
@@ -129,14 +130,14 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Flux<DataBuffer> applyEncoder(Object data, DataBufferFactory bufferFactory) {
|
||||
private <T> Flux<DataBuffer> applyEncoder(Object data, DataBufferFactory bufferFactory, Map<String, Object> hints) {
|
||||
ResolvableType elementType = ResolvableType.forClass(data.getClass());
|
||||
Optional<Encoder<?>> encoder = dataEncoders
|
||||
.stream()
|
||||
.filter(e -> e.canEncode(elementType, MimeTypeUtils.APPLICATION_JSON))
|
||||
.filter(e -> e.canEncode(elementType, MimeTypeUtils.APPLICATION_JSON, Collections.emptyMap()))
|
||||
.findFirst();
|
||||
return ((Encoder<T>) encoder.orElseThrow(() -> new CodecException("No suitable encoder found!")))
|
||||
.encode(Mono.just((T) data), bufferFactory, elementType, MimeTypeUtils.APPLICATION_JSON)
|
||||
.encode(Mono.just((T) data), bufferFactory, elementType, MimeTypeUtils.APPLICATION_JSON, hints)
|
||||
.concatWith(encodeString("\n", bufferFactory));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.client.reactive;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -179,7 +180,7 @@ public class ResponseExtractors {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageReader<?> reader = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Flux<T>) reader.read(responseType, response);
|
||||
return (Flux<T>) reader.read(responseType, response, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -188,14 +189,14 @@ public class ResponseExtractors {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageReader<?> reader = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Mono<T>) reader.readMono(responseType, response);
|
||||
return (Mono<T>) reader.readMono(responseType, response, Collections.emptyMap());
|
||||
}
|
||||
|
||||
protected static HttpMessageReader<?> resolveMessageReader(List<HttpMessageReader<?>> messageReaders,
|
||||
ResolvableType responseType, MediaType contentType) {
|
||||
|
||||
return messageReaders.stream()
|
||||
.filter(e -> e.canRead(responseType, contentType))
|
||||
.filter(e -> e.canRead(responseType, contentType, Collections.emptyMap()))
|
||||
.findFirst()
|
||||
.orElseThrow(() ->
|
||||
new WebClientException(
|
||||
|
||||
@@ -291,13 +291,13 @@ public final class WebClient {
|
||||
"Could not encode request body of type '" + contentType
|
||||
+ "' with target type '" + requestType.toString() + "'"));
|
||||
}
|
||||
return messageWriter.get().write((Publisher) content, requestType, contentType, request);
|
||||
return messageWriter.get().write((Publisher) content, requestType, contentType, request, Collections.emptyMap());
|
||||
}
|
||||
|
||||
protected Optional<HttpMessageWriter<?>> resolveWriter(List<HttpMessageWriter<?>> messageWriters,
|
||||
ResolvableType type, MediaType mediaType) {
|
||||
|
||||
return messageWriters.stream().filter(e -> e.canWrite(type, mediaType)).findFirst();
|
||||
return messageWriters.stream().filter(e -> e.canWrite(type, mediaType, Collections.emptyMap())).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.client.reactive.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.adapter.RxJava1Adapter;
|
||||
@@ -193,7 +194,7 @@ public class RxJava1ResponseExtractors {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageReader<?> converter = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Flux<T>) converter.read(responseType, response);
|
||||
return (Flux<T>) converter.read(responseType, response, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -202,14 +203,14 @@ public class RxJava1ResponseExtractors {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageReader<?> converter = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Mono<T>) converter.readMono(responseType, response);
|
||||
return (Mono<T>) converter.readMono(responseType, response, Collections.emptyMap());
|
||||
}
|
||||
|
||||
protected static HttpMessageReader<?> resolveMessageReader(List<HttpMessageReader<?>> messageReaders,
|
||||
ResolvableType responseType, MediaType contentType) {
|
||||
|
||||
return messageReaders.stream()
|
||||
.filter(e -> e.canRead(responseType, contentType))
|
||||
.filter(e -> e.canRead(responseType, contentType, Collections.emptyMap()))
|
||||
.findFirst()
|
||||
.orElseThrow(() ->
|
||||
new WebClientException(
|
||||
|
||||
Reference in New Issue
Block a user