Skip to content

Commit 5ffe512

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-1038
1 parent d064889 commit 5ffe512

File tree

4 files changed

+57
-22
lines changed

4 files changed

+57
-22
lines changed

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

+14-10
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.http.HttpHeaders;
3333
import org.springframework.http.MediaType;
3434
import org.springframework.util.Assert;
35+
import org.springframework.util.StringUtils;
3536
import org.springframework.web.reactive.function.server.ServerRequest;
3637
import org.springframework.web.reactive.function.server.ServerResponse;
3738
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
@@ -103,16 +104,19 @@ public Mono<ServerResponse> handleRequest(ServerRequest serverRequest) {
103104

104105
private static Mono<SerializableGraphQlRequest> applyApplicationGraphQlFallback(
105106
UnsupportedMediaTypeStatusException ex, ServerRequest request) {
106-
107-
// Spec requires application/json but some clients still use application/graphql
108-
return "application/graphql".equals(request.headers().firstHeader(HttpHeaders.CONTENT_TYPE)) ?
109-
ServerRequest.from(request)
110-
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
111-
.body(request.bodyToFlux(DataBuffer.class))
112-
.build()
113-
.bodyToMono(SerializableGraphQlRequest.class)
114-
.log() :
115-
Mono.error(ex);
107+
String contentTypeHeader = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE);
108+
if (StringUtils.hasText(contentTypeHeader)) {
109+
MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
110+
MediaType applicationGraphQl = MediaType.parseMediaType("application/graphql");
111+
// Spec requires application/json but some clients still use application/graphql
112+
return applicationGraphQl.includes(contentType) ? ServerRequest.from(request)
113+
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
114+
.body(request.bodyToFlux(DataBuffer.class))
115+
.build()
116+
.bodyToMono(SerializableGraphQlRequest.class)
117+
.log() : Mono.error(ex);
118+
}
119+
return Mono.error(ex);
116120
}
117121

118122
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {

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

+18-12
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.util.IdGenerator;
4343
import org.springframework.util.LinkedMultiValueMap;
4444
import org.springframework.util.MultiValueMap;
45+
import org.springframework.util.StringUtils;
4546
import org.springframework.web.HttpMediaTypeNotSupportedException;
4647
import org.springframework.web.server.ServerWebInputException;
4748
import org.springframework.web.servlet.function.ServerRequest;
@@ -154,23 +155,28 @@ private static GraphQlRequest readBody(ServerRequest request) throws ServletExce
154155

155156
private static SerializableGraphQlRequest applyApplicationGraphQlFallback(
156157
ServerRequest request, HttpMediaTypeNotSupportedException ex) throws HttpMediaTypeNotSupportedException {
157-
158-
// Spec requires application/json but some clients still use application/graphql
159-
if ("application/graphql".equals(request.headers().firstHeader(HttpHeaders.CONTENT_TYPE))) {
160-
try {
161-
request = ServerRequest.from(request)
162-
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
163-
.body(request.body(byte[].class))
164-
.build();
165-
return request.body(SerializableGraphQlRequest.class);
166-
}
167-
catch (Throwable ex2) {
168-
// ignore
158+
String contentTypeHeader = request.headers().firstHeader(HttpHeaders.CONTENT_TYPE);
159+
if (StringUtils.hasText(contentTypeHeader)) {
160+
MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
161+
MediaType applicationGraphQl = MediaType.parseMediaType("application/graphql");
162+
// Spec requires application/json but some clients still use application/graphql
163+
if (applicationGraphQl.includes(contentType)) {
164+
try {
165+
request = ServerRequest.from(request)
166+
.headers((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
167+
.body(request.body(byte[].class))
168+
.build();
169+
return request.body(SerializableGraphQlRequest.class);
170+
}
171+
catch (Throwable ex2) {
172+
// ignore
173+
}
169174
}
170175
}
171176
throw ex;
172177
}
173178

179+
174180
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {
175181
for (MediaType accepted : serverRequest.headers().accept()) {
176182
if (SUPPORTED_MEDIA_TYPES.contains(accepted)) {

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

+16
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ void shouldSupportApplicationGraphQl() throws Exception {
9191
.verifyComplete();
9292
}
9393

94+
@Test
95+
void shouldSupportApplicationGraphQlWithCharset() throws Exception {
96+
String document = "{greeting}";
97+
MockServerHttpRequest httpRequest = MockServerHttpRequest.post("/")
98+
.contentType(MediaType.parseMediaType("application/graphql;charset=UTF-8"))
99+
.accept(MediaType.ALL)
100+
.body(initRequestBody(document));
101+
102+
MockServerHttpResponse response = handleRequest(httpRequest, this.greetingHandler);
103+
104+
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
105+
StepVerifier.create(response.getBodyAsString())
106+
.expectNext("{\"data\":{\"greeting\":\"Hello\"}}")
107+
.verifyComplete();
108+
}
109+
94110
@Test
95111
void shouldProduceApplicationGraphQl() throws Exception {
96112
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
@@ -79,6 +79,15 @@ void shouldSupportApplicationGraphQl() throws Exception {
7979
assertThat(response.getContentAsString()).isEqualTo("{\"data\":{\"greeting\":\"Hello\"}}");
8080
}
8181

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

0 commit comments

Comments
 (0)