Support application/graphql with charset information
Prior to this commit, gh-948 added a fallback support for the "application/graphql" content-type sent by clients. This media type is not widely used and advised against by the spec group. This fallback checked for an exact match of the content type, not taking into account potential media type parameters such as charset. This commit ensure that a `MediaType#include` comparison is used to trigger the fallback. Fixes gh-1038
This commit is contained in:
@@ -32,6 +32,7 @@ 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.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
@@ -103,16 +104,19 @@ 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);
|
||||
String contentTypeHeader = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE);
|
||||
if (StringUtils.hasText(contentTypeHeader)) {
|
||||
MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
|
||||
MediaType applicationGraphQl = MediaType.parseMediaType("application/graphql");
|
||||
// Spec requires application/json but some clients still use application/graphql
|
||||
return applicationGraphQl.includes(contentType) ? ServerRequest.from(request)
|
||||
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
|
||||
.body(request.bodyToFlux(DataBuffer.class))
|
||||
.build()
|
||||
.bodyToMono(SerializableGraphQlRequest.class)
|
||||
.log() : Mono.error(ex);
|
||||
}
|
||||
return Mono.error(ex);
|
||||
}
|
||||
|
||||
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.IdGenerator;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
import org.springframework.web.servlet.function.ServerRequest;
|
||||
@@ -154,23 +155,28 @@ public class GraphQlHttpHandler {
|
||||
|
||||
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
|
||||
String contentTypeHeader = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE);
|
||||
if (StringUtils.hasText(contentTypeHeader)) {
|
||||
MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
|
||||
MediaType applicationGraphQl = MediaType.parseMediaType("application/graphql");
|
||||
// Spec requires application/json but some clients still use application/graphql
|
||||
if (applicationGraphQl.includes(contentType)) {
|
||||
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) {
|
||||
for (MediaType accepted : serverRequest.headers().accept()) {
|
||||
if (SUPPORTED_MEDIA_TYPES.contains(accepted)) {
|
||||
|
||||
@@ -91,6 +91,22 @@ public class GraphQlHttpHandlerTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportApplicationGraphQlWithCharset() throws Exception {
|
||||
String document = "{greeting}";
|
||||
MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/")
|
||||
.contentType(MediaType.parseMediaType("application/graphql;charset=UTF-8"))
|
||||
.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("/")
|
||||
|
||||
@@ -79,6 +79,15 @@ public class GraphQlHttpHandlerTests {
|
||||
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportApplicationGraphQlWithCharset() throws Exception {
|
||||
MockHttpServletRequest request = createServletRequest("{ greeting }", "*/*");
|
||||
request.setContentType("application/graphql;charset=UTF-8");
|
||||
|
||||
MockHttpServletResponse response = handleRequest(request, this.greetingHandler);
|
||||
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProduceApplicationGraphQl() throws Exception {
|
||||
MockHttpServletRequest request = createServletRequest("{ greeting }", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE);
|
||||
|
||||
Reference in New Issue
Block a user