Skip to content

Commit abf9ce8

Browse files
committed
Improve handling of empty response with Mono<T>
Issue: SPR-17560
1 parent 63275ae commit abf9ce8

File tree

3 files changed

+28
-13
lines changed

3 files changed

+28
-13
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ public Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType eleme
119119
if (inputStream instanceof Mono) {
120120
HttpHeaders headers = message.getHeaders();
121121
return Mono.from(body)
122-
.defaultIfEmpty(message.bufferFactory().wrap(new byte[0]))
122+
.switchIfEmpty(Mono.defer(() -> {
123+
headers.setContentLength(0);
124+
return message.setComplete().then(Mono.empty());
125+
}))
123126
.flatMap(buffer -> {
124127
headers.setContentLength(buffer.readableByteCount());
125128
return message.writeWith(Mono.just(buffer));

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,17 @@
3434
import org.springframework.core.codec.Encoder;
3535
import org.springframework.core.io.buffer.DataBuffer;
3636
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
37-
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
3837
import org.springframework.http.MediaType;
3938
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
4039
import org.springframework.util.MimeType;
4140
import org.springframework.util.MimeTypeUtils;
4241

43-
import static java.nio.charset.StandardCharsets.ISO_8859_1;
44-
import static java.nio.charset.StandardCharsets.UTF_8;
45-
import static org.junit.Assert.assertEquals;
46-
import static org.junit.Assert.assertFalse;
47-
import static org.junit.Assert.assertTrue;
42+
import static java.nio.charset.StandardCharsets.*;
43+
import static org.junit.Assert.*;
4844
import static org.mockito.ArgumentMatchers.any;
49-
import static org.mockito.Mockito.when;
50-
import static org.springframework.core.ResolvableType.forClass;
51-
import static org.springframework.http.MediaType.TEXT_HTML;
52-
import static org.springframework.http.MediaType.TEXT_PLAIN;
53-
import static org.springframework.http.MediaType.TEXT_XML;
45+
import static org.mockito.Mockito.*;
46+
import static org.springframework.core.ResolvableType.*;
47+
import static org.springframework.http.MediaType.*;
5448

5549
/**
5650
* Unit tests for {@link EncoderHttpMessageWriter}.
@@ -174,7 +168,7 @@ public void setContentLengthForMonoBody() {
174168
public void emptyBodyWritten() {
175169
HttpMessageWriter<String> writer = getWriter(MimeTypeUtils.TEXT_PLAIN);
176170
writer.write(Mono.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block();
177-
StepVerifier.create(this.response.getBody()).expectNextCount(1).verifyComplete();
171+
StepVerifier.create(this.response.getBody()).expectComplete();
178172
assertEquals(0, this.response.getHeaders().getContentLength());
179173
}
180174

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingMessageConversionIntegrationTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,19 @@ public void personResponseBodyWithMono() throws Exception {
149149
assertEquals(expected, responseEntity.getBody());
150150
}
151151

152+
@Test // SPR-17506
153+
public void personResponseBodyWithEmptyMono() throws Exception {
154+
ResponseEntity<Person> responseEntity = performGet("/person-response/mono-empty", JSON, Person.class);
155+
assertEquals(0, responseEntity.getHeaders().getContentLength());
156+
assertNull(responseEntity.getBody());
157+
158+
// As we're on the same connection, the 2nd request proves server response handling
159+
// did complete after the 1st request..
160+
responseEntity = performGet("/person-response/mono-empty", JSON, Person.class);
161+
assertEquals(0, responseEntity.getHeaders().getContentLength());
162+
assertNull(responseEntity.getBody());
163+
}
164+
152165
@Test
153166
public void personResponseBodyWithMonoDeclaredAsObject() throws Exception {
154167
Person expected = new Person("Robert");
@@ -495,6 +508,11 @@ public Mono<Person> getMono() {
495508
return Mono.just(new Person("Robert"));
496509
}
497510

511+
@GetMapping("/mono-empty")
512+
public Mono<Person> getMonoEmpty() {
513+
return Mono.empty();
514+
}
515+
498516
@GetMapping("/mono-declared-as-object")
499517
public Object getMonoDeclaredAsObject() {
500518
return Mono.just(new Person("Robert"));

0 commit comments

Comments
 (0)