Add NDJSON and deprecate application/stream+json

Closes gh-21283
This commit is contained in:
Rossen Stoyanchev
2020-07-28 17:53:12 +03:00
parent 354635ede0
commit 683cc2eb7f
17 changed files with 144 additions and 46 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -73,8 +74,12 @@ class ReactiveTypeHandler {
private static final long STREAMING_TIMEOUT_VALUE = -1;
@SuppressWarnings("deprecation")
private static final List<MediaType> JSON_STREAMING_MEDIA_TYPES =
Arrays.asList(MediaType.APPLICATION_NDJSON, MediaType.APPLICATION_STREAM_JSON);
private static final Log logger = LogFactory.getLog(ReactiveTypeHandler.class);
private static Log logger = LogFactory.getLog(ReactiveTypeHandler.class);
private final ReactiveAdapterRegistry adapterRegistry;
@@ -144,11 +149,15 @@ class ReactiveTypeHandler {
new TextEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
return emitter;
}
if (mediaTypes.stream().anyMatch(MediaType.APPLICATION_STREAM_JSON::includes)) {
logExecutorWarning(returnType);
ResponseBodyEmitter emitter = getEmitter(MediaType.APPLICATION_STREAM_JSON);
new JsonEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
return emitter;
for (MediaType type : mediaTypes) {
for (MediaType streamingType : JSON_STREAMING_MEDIA_TYPES) {
if (streamingType.includes(type)) {
logExecutorWarning(returnType);
ResponseBodyEmitter emitter = getEmitter(streamingType);
new JsonEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
return emitter;
}
}
}
}

View File

@@ -250,7 +250,7 @@
By default, a SimpleAsyncTaskExecutor is used which does not re-use threads and is not recommended for production.
As of 5.0 this executor is also used when a controller returns a reactive type that does streaming
(e.g. "text/event-stream" or "application/stream+json") for the blocking writes to the
(e.g. "text/event-stream" or "application/x-ndjson") for the blocking writes to the
"javax.servlet.ServletOutputStream".
]]></xsd:documentation>

View File

@@ -225,7 +225,7 @@ public class ReactiveTypeHandlerTests {
@Test
public void writeStreamJson() throws Exception {
this.servletRequest.addHeader("Accept", "application/stream+json");
this.servletRequest.addHeader("Accept", "application/x-ndjson");
Sinks.StandaloneFluxSink<Bar> sink = Sinks.unicast();
ResponseBodyEmitter emitter = handleValue(sink.asFlux(), Flux.class, forClass(Bar.class));
@@ -243,7 +243,7 @@ public class ReactiveTypeHandlerTests {
sink.next(bar2);
sink.complete();
assertThat(message.getHeaders().getContentType().toString()).isEqualTo("application/stream+json");
assertThat(message.getHeaders().getContentType().toString()).isEqualTo("application/x-ndjson");
assertThat(emitterHandler.getValues()).isEqualTo(Arrays.asList(bar1, "\n", bar2, "\n"));
}