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
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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 -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user