Refactored BodyInserters
This commit introduces a couple of changes to BodyInserters: - Refactored writeWithMessageWriters into BiFunction - BodyInserters.fromResource now uses ResourceMessagewriter from context - BodyInserters.fromServerSentEvents now uses SseHttpMessageWriter from context
This commit is contained in:
parent
94930043fd
commit
20dec61d04
|
@ -33,6 +33,8 @@ import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||||
import org.springframework.http.codec.HttpMessageReader;
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
import org.springframework.http.codec.HttpMessageWriter;
|
import org.springframework.http.codec.HttpMessageWriter;
|
||||||
|
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||||
|
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
|
||||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||||
|
@ -70,16 +72,25 @@ class DefaultHandlerStrategiesBuilder implements HandlerStrategies.Builder {
|
||||||
messageReader(new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
|
||||||
messageReader(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||||
messageReader(new DecoderHttpMessageReader<>(new StringDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||||
|
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
||||||
|
messageWriter(new ResourceHttpMessageWriter());
|
||||||
|
|
||||||
if (jaxb2Present) {
|
if (jaxb2Present) {
|
||||||
messageReader(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||||
}
|
}
|
||||||
if (jackson2Present) {
|
if (jackson2Present) {
|
||||||
messageReader(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
|
Jackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();
|
||||||
|
messageWriter(new EncoderHttpMessageWriter<>(jsonEncoder));
|
||||||
|
messageWriter(
|
||||||
|
new ServerSentEventHttpMessageWriter(Collections.singletonList(jsonEncoder)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
messageWriter(new ServerSentEventHttpMessageWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,8 @@ import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
|
||||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementations of {@link BodyInserter} that write various bodies, such a reactive streams,
|
* Implementations of {@link BodyInserter} that write various bodies, such a reactive streams,
|
||||||
|
@ -49,12 +47,6 @@ public abstract class BodyInserters {
|
||||||
private static final ResolvableType SERVER_SIDE_EVENT_TYPE =
|
private static final ResolvableType SERVER_SIDE_EVENT_TYPE =
|
||||||
ResolvableType.forClass(ServerSentEvent.class);
|
ResolvableType.forClass(ServerSentEvent.class);
|
||||||
|
|
||||||
private static final boolean jackson2Present =
|
|
||||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
|
||||||
BodyInserters.class.getClassLoader()) &&
|
|
||||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
|
||||||
BodyInserters.class.getClassLoader());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@code BodyInserter} that writes the given single object.
|
* Return a {@code BodyInserter} that writes the given single object.
|
||||||
* @param body the body of the response
|
* @param body the body of the response
|
||||||
|
@ -63,8 +55,7 @@ public abstract class BodyInserters {
|
||||||
public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromObject(T body) {
|
public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromObject(T body) {
|
||||||
Assert.notNull(body, "'body' must not be null");
|
Assert.notNull(body, "'body' must not be null");
|
||||||
return BodyInserter.of(
|
return BodyInserter.of(
|
||||||
(response, context) -> writeWithMessageWriters(response, context,
|
writeFunctionFor(Mono.just(body), ResolvableType.forInstance(body)),
|
||||||
Mono.just(body), ResolvableType.forInstance(body)),
|
|
||||||
() -> body);
|
() -> body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +72,10 @@ public abstract class BodyInserters {
|
||||||
|
|
||||||
Assert.notNull(publisher, "'publisher' must not be null");
|
Assert.notNull(publisher, "'publisher' must not be null");
|
||||||
Assert.notNull(elementClass, "'elementClass' must not be null");
|
Assert.notNull(elementClass, "'elementClass' must not be null");
|
||||||
return fromPublisher(publisher, ResolvableType.forClass(elementClass));
|
return BodyInserter.of(
|
||||||
|
writeFunctionFor(publisher, ResolvableType.forClass(elementClass)),
|
||||||
|
() -> publisher
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,8 +92,7 @@ public abstract class BodyInserters {
|
||||||
Assert.notNull(publisher, "'publisher' must not be null");
|
Assert.notNull(publisher, "'publisher' must not be null");
|
||||||
Assert.notNull(elementType, "'elementType' must not be null");
|
Assert.notNull(elementType, "'elementType' must not be null");
|
||||||
return BodyInserter.of(
|
return BodyInserter.of(
|
||||||
(response, context) -> writeWithMessageWriters(response, context,
|
writeFunctionFor(publisher, elementType),
|
||||||
publisher, elementType),
|
|
||||||
() -> publisher
|
() -> publisher
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -117,15 +110,23 @@ public abstract class BodyInserters {
|
||||||
Assert.notNull(resource, "'resource' must not be null");
|
Assert.notNull(resource, "'resource' must not be null");
|
||||||
return BodyInserter.of(
|
return BodyInserter.of(
|
||||||
(response, context) -> {
|
(response, context) -> {
|
||||||
ResourceHttpMessageWriter messageWriter = new ResourceHttpMessageWriter();
|
HttpMessageWriter<Resource> messageWriter = resourceHttpMessageWriter(context);
|
||||||
MediaType contentType = response.getHeaders().getContentType();
|
return messageWriter.write(Mono.just(resource), RESOURCE_TYPE, null,
|
||||||
return messageWriter.write(Mono.just(resource), RESOURCE_TYPE, contentType,
|
|
||||||
response, Collections.emptyMap());
|
response, Collections.emptyMap());
|
||||||
},
|
},
|
||||||
() -> resource
|
() -> resource
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static HttpMessageWriter<Resource> resourceHttpMessageWriter(BodyInserter.Context context) {
|
||||||
|
return context.messageWriters().get()
|
||||||
|
.filter(messageWriter -> messageWriter.canWrite(RESOURCE_TYPE, null))
|
||||||
|
.findFirst()
|
||||||
|
.map(BodyInserters::<Resource>cast)
|
||||||
|
.orElseThrow(() -> new IllegalStateException(
|
||||||
|
"Could not find HttpMessageWriter that supports Resources."));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@code BodyInserter} that writes the given {@code ServerSentEvent} publisher.
|
* Return a {@code BodyInserter} that writes the given {@code ServerSentEvent} publisher.
|
||||||
* @param eventsPublisher the {@code ServerSentEvent} publisher to write to the response body
|
* @param eventsPublisher the {@code ServerSentEvent} publisher to write to the response body
|
||||||
|
@ -139,10 +140,9 @@ public abstract class BodyInserters {
|
||||||
Assert.notNull(eventsPublisher, "'eventsPublisher' must not be null");
|
Assert.notNull(eventsPublisher, "'eventsPublisher' must not be null");
|
||||||
return BodyInserter.of(
|
return BodyInserter.of(
|
||||||
(response, context) -> {
|
(response, context) -> {
|
||||||
ServerSentEventHttpMessageWriter messageWriter = sseMessageWriter();
|
HttpMessageWriter<ServerSentEvent<T>> messageWriter = sseMessageWriter(context);
|
||||||
MediaType contentType = response.getHeaders().getContentType();
|
|
||||||
return messageWriter.write(eventsPublisher, SERVER_SIDE_EVENT_TYPE,
|
return messageWriter.write(eventsPublisher, SERVER_SIDE_EVENT_TYPE,
|
||||||
contentType, response, Collections.emptyMap());
|
MediaType.TEXT_EVENT_STREAM, response, Collections.emptyMap());
|
||||||
},
|
},
|
||||||
() -> eventsPublisher
|
() -> eventsPublisher
|
||||||
);
|
);
|
||||||
|
@ -183,35 +183,39 @@ public abstract class BodyInserters {
|
||||||
Assert.notNull(eventType, "'eventType' must not be null");
|
Assert.notNull(eventType, "'eventType' must not be null");
|
||||||
return BodyInserter.of(
|
return BodyInserter.of(
|
||||||
(response, context) -> {
|
(response, context) -> {
|
||||||
ServerSentEventHttpMessageWriter messageWriter = sseMessageWriter();
|
HttpMessageWriter<T> messageWriter = sseMessageWriter(context);
|
||||||
MediaType contentType = response.getHeaders().getContentType();
|
return messageWriter.write(eventsPublisher, eventType,
|
||||||
return messageWriter.write(eventsPublisher, eventType, contentType, response,
|
MediaType.TEXT_EVENT_STREAM, response, Collections.emptyMap());
|
||||||
Collections.emptyMap());
|
|
||||||
|
|
||||||
},
|
},
|
||||||
() -> eventsPublisher
|
() -> eventsPublisher
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ServerSentEventHttpMessageWriter sseMessageWriter() {
|
private static <T> HttpMessageWriter<T> sseMessageWriter(BodyInserter.Context context) {
|
||||||
return jackson2Present ? new ServerSentEventHttpMessageWriter(
|
return context.messageWriters().get()
|
||||||
Collections.singletonList(new Jackson2JsonEncoder())) :
|
.filter(messageWriter -> messageWriter
|
||||||
new ServerSentEventHttpMessageWriter();
|
.canWrite(SERVER_SIDE_EVENT_TYPE, MediaType.TEXT_EVENT_STREAM))
|
||||||
|
.findFirst()
|
||||||
|
.map(BodyInserters::<T>cast)
|
||||||
|
.orElseThrow(() -> new IllegalStateException(
|
||||||
|
"Could not find HttpMessageWriter that supports " +
|
||||||
|
MediaType.TEXT_EVENT_STREAM_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Mono<Void> writeWithMessageWriters(ReactiveHttpOutputMessage outputMessage,
|
private static <T, M extends ReactiveHttpOutputMessage> BiFunction<M, BodyInserter.Context, Mono<Void>>
|
||||||
BodyInserter.Context context,
|
writeFunctionFor(Publisher<T> body, ResolvableType bodyType) {
|
||||||
Publisher<T> body,
|
|
||||||
ResolvableType bodyType) {
|
|
||||||
|
|
||||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
return (m, context) -> {
|
||||||
|
|
||||||
|
MediaType contentType = m.getHeaders().getContentType();
|
||||||
Supplier<Stream<HttpMessageWriter<?>>> messageWriters = context.messageWriters();
|
Supplier<Stream<HttpMessageWriter<?>>> messageWriters = context.messageWriters();
|
||||||
return messageWriters.get()
|
return messageWriters.get()
|
||||||
.filter(messageWriter -> messageWriter.canWrite(bodyType, contentType))
|
.filter(messageWriter -> messageWriter.canWrite(bodyType, contentType))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(BodyInserters::cast)
|
.map(BodyInserters::cast)
|
||||||
.map(messageWriter -> messageWriter
|
.map(messageWriter -> messageWriter
|
||||||
.write(body, bodyType, contentType, outputMessage, Collections
|
.write(body, bodyType, contentType, m, Collections
|
||||||
.emptyMap()))
|
.emptyMap()))
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
List<MediaType> supportedMediaTypes = messageWriters.get()
|
List<MediaType> supportedMediaTypes = messageWriters.get()
|
||||||
|
@ -221,6 +225,7 @@ public abstract class BodyInserters {
|
||||||
new UnsupportedMediaTypeException(contentType, supportedMediaTypes);
|
new UnsupportedMediaTypeException(contentType, supportedMediaTypes);
|
||||||
return Mono.error(error);
|
return Mono.error(error);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||||
import org.springframework.http.codec.HttpMessageReader;
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
import org.springframework.http.codec.HttpMessageWriter;
|
import org.springframework.http.codec.HttpMessageWriter;
|
||||||
|
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||||
|
@ -70,9 +71,12 @@ class DefaultWebClientStrategiesBuilder implements WebClientStrategies.Builder {
|
||||||
messageReader(new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
|
||||||
messageReader(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||||
messageReader(new DecoderHttpMessageReader<>(new StringDecoder(false)));
|
messageReader(new DecoderHttpMessageReader<>(new StringDecoder(false)));
|
||||||
|
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
||||||
|
messageWriter(new ResourceHttpMessageWriter());
|
||||||
|
|
||||||
if (jaxb2Present) {
|
if (jaxb2Present) {
|
||||||
messageReader(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
messageReader(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
||||||
messageWriter(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
messageWriter(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.http.codec;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -57,8 +58,13 @@ public class BodyInsertersTests {
|
||||||
final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||||
messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||||
messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
||||||
|
messageWriters.add(new ResourceHttpMessageWriter());
|
||||||
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||||
messageWriters.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
|
Jackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();
|
||||||
|
messageWriters.add(new EncoderHttpMessageWriter<>(jsonEncoder));
|
||||||
|
messageWriters
|
||||||
|
.add(new ServerSentEventHttpMessageWriter(Collections.singletonList(jsonEncoder)));
|
||||||
|
|
||||||
|
|
||||||
this.context = new BodyInserter.Context() {
|
this.context = new BodyInserter.Context() {
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue