Browse Source

Fix SSE with indenting serializer in WebMvc.fn

This commit ensures that HTTP headers like "text/event-stream"
are correctly forwarded to the converter used in
SseServerResponse for proper pretty print handling.

Close gh-30302
pull/33756/head
Sébastien Deleuze 3 years ago
parent
commit
0bad69d5fb
  1. 14
      spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java
  2. 28
      spring-webmvc/src/test/java/org/springframework/web/servlet/function/SseServerResponseTests.java

14
spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java

@ -48,6 +48,7 @@ import org.springframework.web.servlet.ModelAndView;
* <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events</a>. * <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events</a>.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Sebastien Deleuze
* @since 5.3.2 * @since 5.3.2
*/ */
final class SseServerResponse extends AbstractServerResponse { final class SseServerResponse extends AbstractServerResponse {
@ -90,7 +91,7 @@ final class SseServerResponse extends AbstractServerResponse {
} }
DefaultAsyncServerResponse.writeAsync(request, response, result); DefaultAsyncServerResponse.writeAsync(request, response, result);
this.sseConsumer.accept(new DefaultSseBuilder(response, context, result)); this.sseConsumer.accept(new DefaultSseBuilder(response, context, result, this.headers()));
return null; return null;
} }
@ -113,15 +114,19 @@ final class SseServerResponse extends AbstractServerResponse {
private final List<HttpMessageConverter<?>> messageConverters; private final List<HttpMessageConverter<?>> messageConverters;
private final HttpHeaders httpHeaders;
private final StringBuilder builder = new StringBuilder(); private final StringBuilder builder = new StringBuilder();
private boolean sendFailed; private boolean sendFailed;
public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult<?> deferredResult) { public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult<?> deferredResult,
HttpHeaders httpHeaders) {
this.outputMessage = new ServletServerHttpResponse(response); this.outputMessage = new ServletServerHttpResponse(response);
this.deferredResult = deferredResult; this.deferredResult = deferredResult;
this.messageConverters = context.messageConverters(); this.messageConverters = context.messageConverters();
this.httpHeaders = httpHeaders;
} }
@Override @Override
@ -206,7 +211,7 @@ final class SseServerResponse extends AbstractServerResponse {
for (HttpMessageConverter<?> converter : this.messageConverters) { for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canWrite(dataClass, MediaType.APPLICATION_JSON)) { if (converter.canWrite(dataClass, MediaType.APPLICATION_JSON)) {
HttpMessageConverter<Object> objectConverter = (HttpMessageConverter<Object>) converter; HttpMessageConverter<Object> objectConverter = (HttpMessageConverter<Object>) converter;
ServerHttpResponse response = new MutableHeadersServerHttpResponse(this.outputMessage); ServerHttpResponse response = new MutableHeadersServerHttpResponse(this.outputMessage, this.httpHeaders);
objectConverter.write(data, MediaType.APPLICATION_JSON, response); objectConverter.write(data, MediaType.APPLICATION_JSON, response);
this.outputMessage.getBody().write(NL_NL); this.outputMessage.getBody().write(NL_NL);
this.outputMessage.flush(); this.outputMessage.flush();
@ -276,9 +281,10 @@ final class SseServerResponse extends AbstractServerResponse {
private final HttpHeaders mutableHeaders = new HttpHeaders(); private final HttpHeaders mutableHeaders = new HttpHeaders();
public MutableHeadersServerHttpResponse(ServerHttpResponse delegate) { public MutableHeadersServerHttpResponse(ServerHttpResponse delegate, HttpHeaders headers) {
super(delegate); super(delegate);
this.mutableHeaders.putAll(delegate.getHeaders()); this.mutableHeaders.putAll(delegate.getHeaders());
this.mutableHeaders.putAll(headers);
} }
@Override @Override

28
spring-webmvc/src/test/java/org/springframework/web/servlet/function/SseServerResponseTests.java

@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Sebastien Deleuze
*/ */
class SseServerResponseTests { class SseServerResponseTests {
@ -89,6 +90,33 @@ class SseServerResponseTests {
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected); assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
} }
@Test
void sendObjectWithPrettyPrint() throws Exception {
Person person = new Person("John Doe", 42);
ServerResponse response = ServerResponse.sse(sse -> {
try {
sse.send(person);
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setPrettyPrint(true);
ServerResponse.Context context = () -> Collections.singletonList(converter);
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();
String expected = "data:{\n" +
"data: \"name\" : \"John Doe\",\n" +
"data: \"age\" : 42\n" +
"data:}\n" +
"\n";
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
}
@Test @Test
void builder() throws Exception { void builder() throws Exception {
ServerResponse response = ServerResponse.sse(sse -> { ServerResponse response = ServerResponse.sse(sse -> {

Loading…
Cancel
Save