Skip to content

Commit a936984

Browse files
committed
Fix handling for ResponseEntity<Flux<T>> in Spring MVC
Issue: SPR-15456
1 parent 6335449 commit a936984

File tree

2 files changed

+59
-2
lines changed

2 files changed

+59
-2
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType,
126126
response.setStatus(responseEntity.getStatusCodeValue());
127127
outputMessage.getHeaders().putAll(responseEntity.getHeaders());
128128
returnValue = responseEntity.getBody();
129+
returnType = returnType.nested();
129130
if (returnValue == null) {
130131
mavContainer.setRequestHandled(true);
131132
outputMessage.flush();

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222

2323
import org.junit.Before;
2424
import org.junit.Test;
25+
import reactor.core.publisher.EmitterProcessor;
26+
import reactor.core.publisher.Flux;
2527

2628
import org.springframework.core.MethodParameter;
29+
import org.springframework.core.ResolvableType;
2730
import org.springframework.http.ResponseEntity;
2831
import org.springframework.http.converter.HttpMessageConverter;
2932
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -95,6 +98,13 @@ public void supportsReturnTypes() throws Exception {
9598

9699
assertTrue(this.handler.supportsReturnType(
97100
on(TestController.class).resolveReturnType(ResponseEntity.class, ResponseBodyEmitter.class)));
101+
102+
assertTrue(this.handler.supportsReturnType(
103+
on(TestController.class).resolveReturnType(Flux.class, String.class)));
104+
105+
assertTrue(this.handler.supportsReturnType(
106+
on(TestController.class).resolveReturnType(forClassWithGenerics(ResponseEntity.class,
107+
forClassWithGenerics(Flux.class, String.class)))));
98108
}
99109

100110
@Test
@@ -103,8 +113,8 @@ public void doesNotSupportReturnTypes() throws Exception {
103113
assertFalse(this.handler.supportsReturnType(
104114
on(TestController.class).resolveReturnType(ResponseEntity.class, String.class)));
105115

106-
assertFalse(this.handler.supportsReturnType(on(TestController.class)
107-
.resolveReturnType(forClassWithGenerics(ResponseEntity.class,
116+
assertFalse(this.handler.supportsReturnType(
117+
on(TestController.class).resolveReturnType(forClassWithGenerics(ResponseEntity.class,
108118
forClassWithGenerics(AtomicReference.class, String.class)))));
109119

110120
assertFalse(this.handler.supportsReturnType(
@@ -195,6 +205,27 @@ public void sseEmitter() throws Exception {
195205
"\n", this.response.getContentAsString());
196206
}
197207

208+
@Test
209+
public void responseBodyFlux() throws Exception {
210+
211+
this.request.addHeader("Accept", "text/event-stream");
212+
213+
MethodParameter type = on(TestController.class).resolveReturnType(Flux.class, String.class);
214+
EmitterProcessor<String> processor = EmitterProcessor.create();
215+
this.handler.handleReturnValue(processor, type, this.mavContainer, this.webRequest);
216+
217+
assertTrue(this.request.isAsyncStarted());
218+
assertEquals(200, this.response.getStatus());
219+
assertEquals("text/event-stream;charset=UTF-8", this.response.getContentType());
220+
221+
processor.onNext("foo");
222+
processor.onNext("bar");
223+
processor.onNext("baz");
224+
processor.onComplete();
225+
226+
assertEquals("data:foo\n\ndata:bar\n\ndata:baz\n\n", this.response.getContentAsString());
227+
}
228+
198229
@Test
199230
public void responseEntitySse() throws Exception {
200231
MethodParameter type = on(TestController.class).resolveReturnType(ResponseEntity.class, SseEmitter.class);
@@ -218,6 +249,27 @@ public void responseEntitySseNoContent() throws Exception {
218249
assertEquals(Collections.singletonList("bar"), this.response.getHeaders("foo"));
219250
}
220251

252+
@Test
253+
public void responseEntityFlux() throws Exception {
254+
255+
EmitterProcessor<String> processor = EmitterProcessor.create();
256+
ResponseEntity<Flux<String>> entity = ResponseEntity.ok().body(processor);
257+
ResolvableType bodyType = forClassWithGenerics(Flux.class, String.class);
258+
MethodParameter type = on(TestController.class).resolveReturnType(ResponseEntity.class, bodyType);
259+
this.handler.handleReturnValue(entity, type, this.mavContainer, this.webRequest);
260+
261+
assertTrue(this.request.isAsyncStarted());
262+
assertEquals(200, this.response.getStatus());
263+
assertEquals("text/plain", this.response.getContentType());
264+
265+
processor.onNext("foo");
266+
processor.onNext("bar");
267+
processor.onNext("baz");
268+
processor.onComplete();
269+
270+
assertEquals("foobarbaz", this.response.getContentAsString());
271+
}
272+
221273

222274
@SuppressWarnings("unused")
223275
private static class TestController {
@@ -236,6 +288,10 @@ private static class TestController {
236288

237289
private ResponseEntity h7() { return null; }
238290

291+
private Flux<String> h8() { return null; }
292+
293+
private ResponseEntity<Flux<String>> h9() { return null; }
294+
239295
}
240296

241297

0 commit comments

Comments
 (0)