Browse Source

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
pull/22982/head
Rossen Stoyanchev 6 years ago
parent
commit
ed2a3bd451
  1. 20
      spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java
  2. 2
      spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternRouteMatcher.java
  3. 59
      spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java

20
spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java

@ -29,8 +29,8 @@ import org.springframework.core.codec.AbstractEncoder; @@ -29,8 +29,8 @@ import org.springframework.core.codec.AbstractEncoder;
import org.springframework.core.codec.Encoder;
import org.springframework.core.codec.Hints;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpLogging;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
@ -114,24 +114,24 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> { @@ -114,24 +114,24 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
MediaType contentType = updateContentType(message, mediaType);
Flux<DataBuffer> body = this.encoder.encode(
inputStream, message.bufferFactory(), elementType, contentType, hints);
if (inputStream instanceof Mono) {
HttpHeaders headers = message.getHeaders();
return body
.singleOrEmpty()
return Mono.from(inputStream)
.switchIfEmpty(Mono.defer(() -> {
headers.setContentLength(0);
message.getHeaders().setContentLength(0);
return message.setComplete().then(Mono.empty());
}))
.flatMap(buffer -> {
headers.setContentLength(buffer.readableByteCount());
.flatMap(value -> {
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)
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release));
});
}
Flux<DataBuffer> body = this.encoder.encode(
inputStream, message.bufferFactory(), elementType, contentType, hints);
if (isStreamingMediaType(contentType)) {
return message.writeAndFlushWith(body.map(buffer ->
Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)));

2
spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternRouteMatcher.java

@ -103,4 +103,4 @@ public class PathPatternRouteMatcher implements RouteMatcher { @@ -103,4 +103,4 @@ public class PathPatternRouteMatcher implements RouteMatcher {
}
}
}
}

59
spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java

@ -84,13 +84,15 @@ public class EncoderHttpMessageWriterTests { @@ -84,13 +84,15 @@ public class EncoderHttpMessageWriterTests {
@Test
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());
}
@Test
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);
assertTrue(writer.canWrite(forClass(String.class), TEXT_HTML));
@ -99,8 +101,9 @@ public class EncoderHttpMessageWriterTests { @@ -99,8 +101,9 @@ public class EncoderHttpMessageWriterTests {
@Test
public void useNegotiatedMediaType() {
HttpMessageWriter<String> writer = getWriter(MimeTypeUtils.ALL);
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
configureEncoder(TEXT_PLAIN);
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, this.mediaTypeCaptor.getValue());
@ -119,8 +122,9 @@ public class EncoderHttpMessageWriterTests { @@ -119,8 +122,9 @@ public class EncoderHttpMessageWriterTests {
this.mediaTypeCaptor = ArgumentCaptor.forClass(MediaType.class);
MimeType defaultContentType = MimeTypeUtils.TEXT_XML;
HttpMessageWriter<String> writer = getWriter(defaultContentType);
writer.write(Mono.just("body"), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS);
configureEncoder(defaultContentType);
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.mediaTypeCaptor.getValue());
@ -128,8 +132,9 @@ public class EncoderHttpMessageWriterTests { @@ -128,8 +132,9 @@ public class EncoderHttpMessageWriterTests {
@Test
public void useDefaultMediaTypeCharset() {
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
writer.write(Mono.just("body"), forClass(String.class), TEXT_HTML, response, NO_HINTS);
configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML);
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.mediaTypeCaptor.getValue());
@ -138,8 +143,9 @@ public class EncoderHttpMessageWriterTests { @@ -138,8 +143,9 @@ public class EncoderHttpMessageWriterTests {
@Test
public void useNegotiatedMediaTypeCharset() {
MediaType negotiatedMediaType = new MediaType("text", "html", ISO_8859_1);
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
writer.write(Mono.just("body"), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS);
configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML);
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.mediaTypeCaptor.getValue());
@ -150,8 +156,9 @@ public class EncoderHttpMessageWriterTests { @@ -150,8 +156,9 @@ public class EncoderHttpMessageWriterTests {
MediaType outputMessageMediaType = MediaType.TEXT_HTML;
this.response.getHeaders().setContentType(outputMessageMediaType);
HttpMessageWriter<String> writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML);
writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS);
configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML);
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.mediaTypeCaptor.getValue());
@ -161,7 +168,8 @@ public class EncoderHttpMessageWriterTests { @@ -161,7 +168,8 @@ public class EncoderHttpMessageWriterTests {
public void setContentLengthForMonoBody() {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
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();
assertEquals(4, this.response.getHeaders().getContentLength());
@ -180,7 +188,8 @@ public class EncoderHttpMessageWriterTests { @@ -180,7 +188,8 @@ public class EncoderHttpMessageWriterTests {
@Test // SPR-17220
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();
StepVerifier.create(this.response.getBody()).expectComplete();
assertEquals(0, this.response.getHeaders().getContentLength());
@ -188,25 +197,35 @@ public class EncoderHttpMessageWriterTests { @@ -188,25 +197,35 @@ public class EncoderHttpMessageWriterTests {
@Test // gh-22936
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"));
given(this.encoder.getStreamingMediaTypes()).willReturn(Arrays.asList(streamingMediaType));
HttpMessageWriter<String> writer = new EncoderHttpMessageWriter<>(this.encoder);
Method method = ReflectionUtils.findMethod(writer.getClass(), "isStreamingMediaType", MediaType.class);
ReflectionUtils.makeAccessible(method);
assertTrue((Boolean) method.invoke(writer, streamingMediaType));
assertFalse((Boolean) method.invoke(writer, new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "false"))));
assertFalse((Boolean) method.invoke(writer, TEXT_HTML));
}
private HttpMessageWriter<String> getWriter(MimeType... mimeTypes) {
return getWriter(Flux.empty(), mimeTypes);
private void configureEncoder(MimeType... mimeTypes) {
configureEncoder(Flux.empty(), mimeTypes);
}
private void configureEncoder(Flux<DataBuffer> encodedStream, MimeType... mimeTypes) {
List<MimeType> typeList = Arrays.asList(mimeTypes);
given(this.encoder.getEncodableMimeTypes()).willReturn(typeList);
given(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any()))
.willReturn(encodedStream);
}
private HttpMessageWriter<String> getWriter(Flux<DataBuffer> encodedStream, MimeType... mimeTypes) {
private void configureEncoder(DataBuffer dataBuffer, MimeType... mimeTypes) {
List<MimeType> typeList = Arrays.asList(mimeTypes);
given(this.encoder.getEncodableMimeTypes()).willReturn(typeList);
given(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any())).willReturn(encodedStream);
return new EncoderHttpMessageWriter<>(this.encoder);
given(this.encoder.encodeValue(any(), any(), any(), this.mediaTypeCaptor.capture(), any()))
.willReturn(dataBuffer);
}
}

Loading…
Cancel
Save