Skip to content

Commit 66cece6

Browse files
committed
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-1036
1 parent e455b69 commit 66cece6

File tree

4 files changed

+57
-20
lines changed

4 files changed

+57
-20
lines changed

spring-graphql/src/main/java/org/springframework/graphql/server/webflux/AbstractGraphQlHttpHandler.java

+15-9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.lang.Nullable;
3737
import org.springframework.util.Assert;
3838
import org.springframework.util.CollectionUtils;
39+
import org.springframework.util.StringUtils;
3940
import org.springframework.web.reactive.function.server.ServerRequest;
4041
import org.springframework.web.reactive.function.server.ServerResponse;
4142
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
@@ -114,15 +115,20 @@ private Mono<SerializableGraphQlRequest> readRequest(ServerRequest serverRequest
114115
private static Mono<SerializableGraphQlRequest> applyApplicationGraphQlFallback(
115116
UnsupportedMediaTypeStatusException ex, ServerRequest request) {
116117

117-
// Spec requires application/json but some clients still use application/graphql
118-
return "application/graphql".equals(request.headers().firstHeader(HttpHeaders.CONTENT_TYPE)) ?
119-
ServerRequest.from(request)
120-
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
121-
.body(request.bodyToFlux(DataBuffer.class))
122-
.build()
123-
.bodyToMono(SerializableGraphQlRequest.class)
124-
.log() :
125-
Mono.error(ex);
118+
String contentTypeHeader = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE);
119+
if (StringUtils.hasText(contentTypeHeader)) {
120+
MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
121+
MediaType applicationGraphQl = MediaType.parseMediaType("application/graphql");
122+
123+
// Spec requires application/json but some clients still use application/graphql
124+
return applicationGraphQl.includes(contentType) ? ServerRequest.from(request)
125+
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
126+
.body(request.bodyToFlux(DataBuffer.class))
127+
.build()
128+
.bodyToMono(SerializableGraphQlRequest.class)
129+
.log() : Mono.error(ex);
130+
}
131+
return Mono.error(ex);
126132
}
127133

128134
/**

spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/AbstractGraphQlHttpHandler.java

+17-11
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.util.IdGenerator;
5050
import org.springframework.util.LinkedMultiValueMap;
5151
import org.springframework.util.MultiValueMap;
52+
import org.springframework.util.StringUtils;
5253
import org.springframework.web.HttpMediaTypeNotSupportedException;
5354
import org.springframework.web.server.ServerWebInputException;
5455
import org.springframework.web.servlet.ModelAndView;
@@ -170,17 +171,22 @@ private GraphQlRequest readBody(ServerRequest request) throws ServletException {
170171
private static SerializableGraphQlRequest applyApplicationGraphQlFallback(
171172
ServerRequest request, HttpMediaTypeNotSupportedException ex) throws HttpMediaTypeNotSupportedException {
172173

173-
// Spec requires application/json but some clients still use application/graphql
174-
if ("application/graphql".equals(request.headers().firstHeader(HttpHeaders.CONTENT_TYPE))) {
175-
try {
176-
request = ServerRequest.from(request)
177-
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
178-
.body(request.body(byte[].class))
179-
.build();
180-
return request.body(SerializableGraphQlRequest.class);
181-
}
182-
catch (Throwable ex2) {
183-
// ignore
174+
String contentTypeHeader = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE);
175+
if (StringUtils.hasText(contentTypeHeader)) {
176+
MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
177+
MediaType applicationGraphQl = MediaType.parseMediaType("application/graphql");
178+
// Spec requires application/json but some clients still use application/graphql
179+
if (applicationGraphQl.includes(contentType)) {
180+
try {
181+
request = ServerRequest.from(request)
182+
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
183+
.body(request.body(byte[].class))
184+
.build();
185+
return request.body(SerializableGraphQlRequest.class);
186+
}
187+
catch (Throwable ex2) {
188+
// ignore
189+
}
184190
}
185191
}
186192
throw ex;

spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlHttpHandlerTests.java

+16
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,22 @@ void shouldSupportApplicationGraphQl() throws Exception {
9999
.verifyComplete();
100100
}
101101

102+
@Test
103+
void shouldSupportApplicationGraphQlWithCharset() throws Exception {
104+
String document = "{greeting}";
105+
MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/")
106+
.contentType(MediaType.parseMediaType("application/graphql;charset=UTF-8"))
107+
.accept(MediaType.ALL)
108+
.body(initRequestBody(document));
109+
110+
MockServerHttpResponse response = handleRequest(httpRequest, this.greetingHandler);
111+
112+
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
113+
StepVerifier.create(response.getBodyAsString())
114+
.expectNext("{\"data\":{\"greeting\":\"Hello\"}}")
115+
.verifyComplete();
116+
}
117+
102118
@Test
103119
void shouldProduceApplicationGraphQl() throws Exception {
104120
MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/")

spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandlerTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ void shouldSupportApplicationGraphQl() throws Exception {
8080
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
8181
}
8282

83+
@Test
84+
void shouldSupportApplicationGraphQlWithCharset() throws Exception {
85+
MockHttpServletRequest request = createServletRequest("{ greeting }", "*/*");
86+
request.setContentType("application/graphql;charset=UTF-8");
87+
88+
MockHttpServletResponse response = handleRequest(request, this.greetingHandler);
89+
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
90+
}
91+
8392
@Test
8493
void shouldProduceApplicationGraphQl() throws Exception {
8594
MockHttpServletRequest request = createServletRequest("{ greeting }", MediaType.APPLICATION_GRAPHQL_RESPONSE_VALUE);

0 commit comments

Comments
 (0)