From a93698487e8c5b87685af58dd41eb6dd79c02158 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 20 Apr 2017 08:12:09 -0400 Subject: [PATCH] Fix handling for ResponseEntity> in Spring MVC Issue: SPR-15456 --- ...ResponseBodyEmitterReturnValueHandler.java | 1 + ...nseBodyEmitterReturnValueHandlerTests.java | 60 ++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java index f09da9eac17..73aec7c9c92 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java @@ -126,6 +126,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur response.setStatus(responseEntity.getStatusCodeValue()); outputMessage.getHeaders().putAll(responseEntity.getHeaders()); returnValue = responseEntity.getBody(); + returnType = returnType.nested(); if (returnValue == null) { mavContainer.setRequestHandled(true); outputMessage.flush(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java index 6b414814116..5e29fe10421 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandlerTests.java @@ -22,8 +22,11 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; import org.junit.Test; +import reactor.core.publisher.EmitterProcessor; +import reactor.core.publisher.Flux; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; @@ -95,6 +98,13 @@ public class ResponseBodyEmitterReturnValueHandlerTests { assertTrue(this.handler.supportsReturnType( on(TestController.class).resolveReturnType(ResponseEntity.class, ResponseBodyEmitter.class))); + + assertTrue(this.handler.supportsReturnType( + on(TestController.class).resolveReturnType(Flux.class, String.class))); + + assertTrue(this.handler.supportsReturnType( + on(TestController.class).resolveReturnType(forClassWithGenerics(ResponseEntity.class, + forClassWithGenerics(Flux.class, String.class))))); } @Test @@ -103,8 +113,8 @@ public class ResponseBodyEmitterReturnValueHandlerTests { assertFalse(this.handler.supportsReturnType( on(TestController.class).resolveReturnType(ResponseEntity.class, String.class))); - assertFalse(this.handler.supportsReturnType(on(TestController.class) - .resolveReturnType(forClassWithGenerics(ResponseEntity.class, + assertFalse(this.handler.supportsReturnType( + on(TestController.class).resolveReturnType(forClassWithGenerics(ResponseEntity.class, forClassWithGenerics(AtomicReference.class, String.class))))); assertFalse(this.handler.supportsReturnType( @@ -195,6 +205,27 @@ public class ResponseBodyEmitterReturnValueHandlerTests { "\n", this.response.getContentAsString()); } + @Test + public void responseBodyFlux() throws Exception { + + this.request.addHeader("Accept", "text/event-stream"); + + MethodParameter type = on(TestController.class).resolveReturnType(Flux.class, String.class); + EmitterProcessor processor = EmitterProcessor.create(); + this.handler.handleReturnValue(processor, type, this.mavContainer, this.webRequest); + + assertTrue(this.request.isAsyncStarted()); + assertEquals(200, this.response.getStatus()); + assertEquals("text/event-stream;charset=UTF-8", this.response.getContentType()); + + processor.onNext("foo"); + processor.onNext("bar"); + processor.onNext("baz"); + processor.onComplete(); + + assertEquals("data:foo\n\ndata:bar\n\ndata:baz\n\n", this.response.getContentAsString()); + } + @Test public void responseEntitySse() throws Exception { MethodParameter type = on(TestController.class).resolveReturnType(ResponseEntity.class, SseEmitter.class); @@ -218,6 +249,27 @@ public class ResponseBodyEmitterReturnValueHandlerTests { assertEquals(Collections.singletonList("bar"), this.response.getHeaders("foo")); } + @Test + public void responseEntityFlux() throws Exception { + + EmitterProcessor processor = EmitterProcessor.create(); + ResponseEntity> entity = ResponseEntity.ok().body(processor); + ResolvableType bodyType = forClassWithGenerics(Flux.class, String.class); + MethodParameter type = on(TestController.class).resolveReturnType(ResponseEntity.class, bodyType); + this.handler.handleReturnValue(entity, type, this.mavContainer, this.webRequest); + + assertTrue(this.request.isAsyncStarted()); + assertEquals(200, this.response.getStatus()); + assertEquals("text/plain", this.response.getContentType()); + + processor.onNext("foo"); + processor.onNext("bar"); + processor.onNext("baz"); + processor.onComplete(); + + assertEquals("foobarbaz", this.response.getContentAsString()); + } + @SuppressWarnings("unused") private static class TestController { @@ -236,6 +288,10 @@ public class ResponseBodyEmitterReturnValueHandlerTests { private ResponseEntity h7() { return null; } + private Flux h8() { return null; } + + private ResponseEntity> h9() { return null; } + }