Take advantage of Encoder#encodeValue
EncoderHttpMessageWriter takes advantage of the Encoder#encodeValue that's new in 5.2 in order to produce a Mono<DataBuffer> instead of producing a Flux<DataBuffer> and then using flux.singleOrEmpty(). Closes gh-22952
This commit is contained in:
@@ -29,8 +29,8 @@ import org.springframework.core.codec.AbstractEncoder;
|
|||||||
import org.springframework.core.codec.Encoder;
|
import org.springframework.core.codec.Encoder;
|
||||||
import org.springframework.core.codec.Hints;
|
import org.springframework.core.codec.Hints;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||||
import org.springframework.core.io.buffer.PooledDataBuffer;
|
import org.springframework.core.io.buffer.PooledDataBuffer;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpLogging;
|
import org.springframework.http.HttpLogging;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||||
@@ -114,24 +114,24 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
|||||||
|
|
||||||
MediaType contentType = updateContentType(message, mediaType);
|
MediaType contentType = updateContentType(message, mediaType);
|
||||||
|
|
||||||
Flux<DataBuffer> body = this.encoder.encode(
|
|
||||||
inputStream, message.bufferFactory(), elementType, contentType, hints);
|
|
||||||
|
|
||||||
if (inputStream instanceof Mono) {
|
if (inputStream instanceof Mono) {
|
||||||
HttpHeaders headers = message.getHeaders();
|
return Mono.from(inputStream)
|
||||||
return body
|
|
||||||
.singleOrEmpty()
|
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
.switchIfEmpty(Mono.defer(() -> {
|
||||||
headers.setContentLength(0);
|
message.getHeaders().setContentLength(0);
|
||||||
return message.setComplete().then(Mono.empty());
|
return message.setComplete().then(Mono.empty());
|
||||||
}))
|
}))
|
||||||
.flatMap(buffer -> {
|
.flatMap(value -> {
|
||||||
headers.setContentLength(buffer.readableByteCount());
|
DataBufferFactory factory = message.bufferFactory();
|
||||||
|
DataBuffer buffer = this.encoder.encodeValue(value, factory, elementType, contentType, hints);
|
||||||
|
message.getHeaders().setContentLength(buffer.readableByteCount());
|
||||||
return message.writeWith(Mono.just(buffer)
|
return message.writeWith(Mono.just(buffer)
|
||||||
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release));
|
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Flux<DataBuffer> body = this.encoder.encode(
|
||||||
|
inputStream, message.bufferFactory(), elementType, contentType, hints);
|
||||||
|
|
||||||
if (isStreamingMediaType(contentType)) {
|
if (isStreamingMediaType(contentType)) {
|
||||||
return message.writeAndFlushWith(body.map(buffer ->
|
return message.writeAndFlushWith(body.map(buffer ->
|
||||||
Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)));
|
Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)));
|
||||||
|
|||||||
@@ -103,4 +103,4 @@ public class PathPatternRouteMatcher implements RouteMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,13 +84,15 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWritableMediaTypes() {
|
public void getWritableMediaTypes() {
|
||||||
HttpMessageWriter<?> writer = getWriter(MimeTypeUtils.TEXT_HTML, MimeTypeUtils.TEXT_XML);
|
configureEncoder(MimeTypeUtils.TEXT_HTML, MimeTypeUtils.TEXT_XML);
|
||||||
|
HttpMessageWriter<?> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
assertEquals(Arrays.asList(TEXT_HTML, TEXT_XML), writer.getWritableMediaTypes());
|
assertEquals(Arrays.asList(TEXT_HTML, TEXT_XML), writer.getWritableMediaTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canWrite() {
|
public void canWrite() {
|
||||||
HttpMessageWriter<?> writer = getWriter(MimeTypeUtils.TEXT_HTML);
|
configureEncoder(MimeTypeUtils.TEXT_HTML);
|
||||||
|
HttpMessageWriter<?> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
given(this.encoder.canEncode(forClass(String.class), TEXT_HTML)).willReturn(true);
|
given(this.encoder.canEncode(forClass(String.class), TEXT_HTML)).willReturn(true);
|
||||||
|
|
||||||
assertTrue(writer.canWrite(forClass(String.class), TEXT_HTML));
|
assertTrue(writer.canWrite(forClass(String.class), TEXT_HTML));
|
||||||
@@ -99,8 +101,9 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void useNegotiatedMediaType() {
|
public void useNegotiatedMediaType() {
|
||||||
HttpMessageWriter<String> writer = getWriter(MimeTypeUtils.ALL);
|
configureEncoder(TEXT_PLAIN);
|
||||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
|
writer.write(Flux.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
|
||||||
|
|
||||||
assertEquals(TEXT_PLAIN, response.getHeaders().getContentType());
|
assertEquals(TEXT_PLAIN, response.getHeaders().getContentType());
|
||||||
assertEquals(TEXT_PLAIN, this.mediaTypeCaptor.getValue());
|
assertEquals(TEXT_PLAIN, this.mediaTypeCaptor.getValue());
|
||||||
@@ -119,8 +122,9 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
this.mediaTypeCaptor = ArgumentCaptor.forClass(MediaType.class);
|
this.mediaTypeCaptor = ArgumentCaptor.forClass(MediaType.class);
|
||||||
|
|
||||||
MimeType defaultContentType = MimeTypeUtils.TEXT_XML;
|
MimeType defaultContentType = MimeTypeUtils.TEXT_XML;
|
||||||
HttpMessageWriter<String> writer = getWriter(defaultContentType);
|
configureEncoder(defaultContentType);
|
||||||
writer.write(Mono.just("body"), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS);
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
|
writer.write(Flux.empty(), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS);
|
||||||
|
|
||||||
assertEquals(defaultContentType, this.response.getHeaders().getContentType());
|
assertEquals(defaultContentType, this.response.getHeaders().getContentType());
|
||||||
assertEquals(defaultContentType, this.mediaTypeCaptor.getValue());
|
assertEquals(defaultContentType, this.mediaTypeCaptor.getValue());
|
||||||
@@ -128,8 +132,9 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void useDefaultMediaTypeCharset() {
|
public void useDefaultMediaTypeCharset() {
|
||||||
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
||||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_HTML, response, NO_HINTS);
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
|
writer.write(Flux.empty(), forClass(String.class), TEXT_HTML, response, NO_HINTS);
|
||||||
|
|
||||||
assertEquals(new MediaType("text", "html", UTF_8), this.response.getHeaders().getContentType());
|
assertEquals(new MediaType("text", "html", UTF_8), this.response.getHeaders().getContentType());
|
||||||
assertEquals(new MediaType("text", "html", UTF_8), this.mediaTypeCaptor.getValue());
|
assertEquals(new MediaType("text", "html", UTF_8), this.mediaTypeCaptor.getValue());
|
||||||
@@ -138,8 +143,9 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
@Test
|
@Test
|
||||||
public void useNegotiatedMediaTypeCharset() {
|
public void useNegotiatedMediaTypeCharset() {
|
||||||
MediaType negotiatedMediaType = new MediaType("text", "html", ISO_8859_1);
|
MediaType negotiatedMediaType = new MediaType("text", "html", ISO_8859_1);
|
||||||
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
||||||
writer.write(Mono.just("body"), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS);
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
|
writer.write(Flux.empty(), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS);
|
||||||
|
|
||||||
assertEquals(negotiatedMediaType, this.response.getHeaders().getContentType());
|
assertEquals(negotiatedMediaType, this.response.getHeaders().getContentType());
|
||||||
assertEquals(negotiatedMediaType, this.mediaTypeCaptor.getValue());
|
assertEquals(negotiatedMediaType, this.mediaTypeCaptor.getValue());
|
||||||
@@ -150,8 +156,9 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
MediaType outputMessageMediaType = MediaType.TEXT_HTML;
|
MediaType outputMessageMediaType = MediaType.TEXT_HTML;
|
||||||
this.response.getHeaders().setContentType(outputMessageMediaType);
|
this.response.getHeaders().setContentType(outputMessageMediaType);
|
||||||
|
|
||||||
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML);
|
||||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
|
writer.write(Flux.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
|
||||||
|
|
||||||
assertEquals(outputMessageMediaType, this.response.getHeaders().getContentType());
|
assertEquals(outputMessageMediaType, this.response.getHeaders().getContentType());
|
||||||
assertEquals(outputMessageMediaType, this.mediaTypeCaptor.getValue());
|
assertEquals(outputMessageMediaType, this.mediaTypeCaptor.getValue());
|
||||||
@@ -161,7 +168,8 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
public void setContentLengthForMonoBody() {
|
public void setContentLengthForMonoBody() {
|
||||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||||
DataBuffer buffer = factory.wrap("body".getBytes(StandardCharsets.UTF_8));
|
DataBuffer buffer = factory.wrap("body".getBytes(StandardCharsets.UTF_8));
|
||||||
HttpMessageWriter<String> writer = getWriter(Flux.just(buffer), MimeTypeUtils.TEXT_PLAIN);
|
configureEncoder(buffer, MimeTypeUtils.TEXT_PLAIN);
|
||||||
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
|
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
|
||||||
|
|
||||||
assertEquals(4, this.response.getHeaders().getContentLength());
|
assertEquals(4, this.response.getHeaders().getContentLength());
|
||||||
@@ -180,7 +188,8 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
|
|
||||||
@Test // SPR-17220
|
@Test // SPR-17220
|
||||||
public void emptyBodyWritten() {
|
public void emptyBodyWritten() {
|
||||||
HttpMessageWriter<String> writer = getWriter(MimeTypeUtils.TEXT_PLAIN);
|
configureEncoder(MimeTypeUtils.TEXT_PLAIN);
|
||||||
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
writer.write(Mono.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
|
writer.write(Mono.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
|
||||||
StepVerifier.create(this.response.getBody()).expectComplete();
|
StepVerifier.create(this.response.getBody()).expectComplete();
|
||||||
assertEquals(0, this.response.getHeaders().getContentLength());
|
assertEquals(0, this.response.getHeaders().getContentLength());
|
||||||
@@ -188,25 +197,35 @@ public class EncoderHttpMessageWriterTests {
|
|||||||
|
|
||||||
@Test // gh-22936
|
@Test // gh-22936
|
||||||
public void isStreamingMediaType() throws InvocationTargetException, IllegalAccessException {
|
public void isStreamingMediaType() throws InvocationTargetException, IllegalAccessException {
|
||||||
HttpMessageWriter<String> writer = getWriter(TEXT_HTML);
|
configureEncoder(TEXT_HTML);
|
||||||
MediaType streamingMediaType = new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "true"));
|
MediaType streamingMediaType = new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "true"));
|
||||||
given(this.encoder.getStreamingMediaTypes()).willReturn(Arrays.asList(streamingMediaType));
|
given(this.encoder.getStreamingMediaTypes()).willReturn(Arrays.asList(streamingMediaType));
|
||||||
|
|
||||||
|
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
|
||||||
Method method = ReflectionUtils.findMethod(writer.getClass(), "isStreamingMediaType", MediaType.class);
|
Method method = ReflectionUtils.findMethod(writer.getClass(), "isStreamingMediaType", MediaType.class);
|
||||||
ReflectionUtils.makeAccessible(method);
|
ReflectionUtils.makeAccessible(method);
|
||||||
|
|
||||||
assertTrue((Boolean) method.invoke(writer, streamingMediaType));
|
assertTrue((Boolean) method.invoke(writer, streamingMediaType));
|
||||||
assertFalse((Boolean) method.invoke(writer, new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "false"))));
|
assertFalse((Boolean) method.invoke(writer, new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "false"))));
|
||||||
assertFalse((Boolean) method.invoke(writer, TEXT_HTML));
|
assertFalse((Boolean) method.invoke(writer, TEXT_HTML));
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageWriter<String> getWriter(MimeType... mimeTypes) {
|
private void configureEncoder(MimeType... mimeTypes) {
|
||||||
return getWriter(Flux.empty(), mimeTypes);
|
configureEncoder(Flux.empty(), mimeTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageWriter<String> getWriter(Flux<DataBuffer> encodedStream, MimeType... mimeTypes) {
|
private void configureEncoder(Flux<DataBuffer> encodedStream, MimeType... mimeTypes) {
|
||||||
List<MimeType> typeList = Arrays.asList(mimeTypes);
|
List<MimeType> typeList = Arrays.asList(mimeTypes);
|
||||||
given(this.encoder.getEncodableMimeTypes()).willReturn(typeList);
|
given(this.encoder.getEncodableMimeTypes()).willReturn(typeList);
|
||||||
given(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any())).willReturn(encodedStream);
|
given(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any()))
|
||||||
return new EncoderHttpMessageWriter<>(this.encoder);
|
.willReturn(encodedStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureEncoder(DataBuffer dataBuffer, MimeType... mimeTypes) {
|
||||||
|
List<MimeType> typeList = Arrays.asList(mimeTypes);
|
||||||
|
given(this.encoder.getEncodableMimeTypes()).willReturn(typeList);
|
||||||
|
given(this.encoder.encodeValue(any(), any(), any(), this.mediaTypeCaptor.capture(), any()))
|
||||||
|
.willReturn(dataBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user