Support application/graphql for request body
Closes gh-948
This commit is contained in:
@@ -25,13 +25,16 @@ import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.graphql.server.WebGraphQlHandler;
|
||||
import org.springframework.graphql.server.WebGraphQlRequest;
|
||||
import org.springframework.graphql.server.support.SerializableGraphQlRequest;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
|
||||
/**
|
||||
* WebFlux.fn Handler for GraphQL over HTTP requests.
|
||||
@@ -73,6 +76,9 @@ public class GraphQlHttpHandler {
|
||||
*/
|
||||
public Mono<ServerResponse> handleRequest(ServerRequest serverRequest) {
|
||||
return serverRequest.bodyToMono(SerializableGraphQlRequest.class)
|
||||
.onErrorResume(
|
||||
UnsupportedMediaTypeStatusException.class,
|
||||
(ex) -> applyApplicationGraphQlFallback(ex, serverRequest))
|
||||
.flatMap((body) -> {
|
||||
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
|
||||
serverRequest.uri(), serverRequest.headers().asHttpHeaders(),
|
||||
@@ -95,6 +101,20 @@ public class GraphQlHttpHandler {
|
||||
});
|
||||
}
|
||||
|
||||
private static Mono<SerializableGraphQlRequest> applyApplicationGraphQlFallback(
|
||||
UnsupportedMediaTypeStatusException ex, ServerRequest request) {
|
||||
|
||||
// Spec requires application/json but some clients still use application/graphql
|
||||
return "application/graphql".equals(request.headers().firstHeader(HttpHeaders.CONTENT_TYPE)) ?
|
||||
ServerRequest.from(request)
|
||||
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
|
||||
.body(request.bodyToFlux(DataBuffer.class))
|
||||
.build()
|
||||
.bodyToMono(SerializableGraphQlRequest.class)
|
||||
.log() :
|
||||
Mono.error(ex);
|
||||
}
|
||||
|
||||
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {
|
||||
for (MediaType accepted : serverRequest.headers().accept()) {
|
||||
if (SUPPORTED_MEDIA_TYPES.contains(accepted)) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.graphql.server.WebGraphQlHandler;
|
||||
import org.springframework.graphql.server.WebGraphQlRequest;
|
||||
import org.springframework.graphql.server.support.SerializableGraphQlRequest;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.AlternativeJdkIdGenerator;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -146,6 +147,28 @@ public class GraphQlHttpHandler {
|
||||
catch (IOException ex) {
|
||||
throw new ServerWebInputException("I/O error while reading request body", null, ex);
|
||||
}
|
||||
catch (HttpMediaTypeNotSupportedException ex) {
|
||||
return applyApplicationGraphQlFallback(request, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static SerializableGraphQlRequest applyApplicationGraphQlFallback(
|
||||
ServerRequest request, HttpMediaTypeNotSupportedException ex) throws HttpMediaTypeNotSupportedException {
|
||||
|
||||
// Spec requires application/json but some clients still use application/graphql
|
||||
if ("application/graphql".equals(request.headers().firstHeader(HttpHeaders.CONTENT_TYPE))) {
|
||||
try {
|
||||
request = ServerRequest.from(request)
|
||||
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
|
||||
.body(request.body(byte[].class))
|
||||
.build();
|
||||
return request.body(SerializableGraphQlRequest.class);
|
||||
}
|
||||
catch (Throwable ex2) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
|
||||
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {
|
||||
|
||||
@@ -75,6 +75,22 @@ public class GraphQlHttpHandlerTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportApplicationGraphQl() throws Exception {
|
||||
String document = "{greeting}";
|
||||
MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/")
|
||||
.contentType(MediaType.parseMediaType("application/graphql"))
|
||||
.accept(MediaType.ALL)
|
||||
.body(initRequestBody(document));
|
||||
|
||||
MockServerHttpResponse response = handleRequest(httpRequest, this.greetingHandler);
|
||||
|
||||
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
|
||||
StepVerifier.create(response.getBodyAsString())
|
||||
.expectNext("{\"data\":{\"greeting\":\"Hello\"}}")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProduceApplicationGraphQl() throws Exception {
|
||||
MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/")
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.server.support.SerializableGraphQlRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -55,7 +56,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
public class GraphQlHttpHandlerTests {
|
||||
|
||||
private static final List<HttpMessageConverter<?>> MESSAGE_READERS =
|
||||
Collections.singletonList(new MappingJackson2HttpMessageConverter());
|
||||
List.of(new MappingJackson2HttpMessageConverter(), new ByteArrayHttpMessageConverter());
|
||||
|
||||
private final GraphQlHttpHandler greetingHandler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) -> "Hello").toHttpHandler();
|
||||
@@ -66,6 +67,16 @@ public class GraphQlHttpHandlerTests {
|
||||
MockHttpServletRequest request = createServletRequest("{ greeting }", "*/*");
|
||||
MockHttpServletResponse response = handleRequest(request, this.greetingHandler);
|
||||
assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportApplicationGraphQl() throws Exception {
|
||||
MockHttpServletRequest request = createServletRequest("{ greeting }", "*/*");
|
||||
request.setContentType("application/graphql");
|
||||
|
||||
MockHttpServletResponse response = handleRequest(request, this.greetingHandler);
|
||||
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user