Skip to content

Commit 1738e0c

Browse files
committed
Reply with HTTP 415 for unsupported GraphQL content-type
Prior to this commit, the configured GraphQL routes would reply with an HTTP 404 status when a POST request is sent with an unsupported content type, such as "text/plain". While such requests are not supported in the first place, we should help developers and let them know that the content type sent is the problem. This commit configures new routes that reply with HTTP 415 "Unsupported Media Type" for these cases. Closes gh-41675
1 parent e2a984c commit 1738e0c

File tree

4 files changed

+50
-2
lines changed

4 files changed

+50
-2
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.springframework.http.HttpHeaders;
5656
import org.springframework.http.HttpMethod;
5757
import org.springframework.http.HttpStatus;
58+
import org.springframework.http.MediaType;
5859
import org.springframework.http.codec.ServerCodecConfigurer;
5960
import org.springframework.web.cors.CorsConfiguration;
6061
import org.springframework.web.reactive.HandlerMapping;
@@ -112,6 +113,7 @@ public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler h
112113
RouterFunctions.Builder builder = RouterFunctions.route();
113114
builder.route(GraphQlRequestPredicates.graphQlHttp(path), httpHandler::handleRequest);
114115
builder.route(GraphQlRequestPredicates.graphQlSse(path), sseHandler::handleRequest);
116+
builder.POST(path, this::unsupportedMediaType);
115117
builder.GET(path, this::onlyAllowPost);
116118
if (properties.getGraphiql().isEnabled()) {
117119
GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
@@ -124,6 +126,14 @@ public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler h
124126
return builder.build();
125127
}
126128

129+
private Mono<ServerResponse> unsupportedMediaType(ServerRequest request) {
130+
return ServerResponse.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).headers(this::acceptJson).build();
131+
}
132+
133+
private void acceptJson(HttpHeaders headers) {
134+
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
135+
}
136+
127137
private Mono<ServerResponse> onlyAllowPost(ServerRequest request) {
128138
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED).headers(this::onlyAllowPost).build();
129139
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler h
117117
RouterFunctions.Builder builder = RouterFunctions.route();
118118
builder.route(GraphQlRequestPredicates.graphQlHttp(path), httpHandler::handleRequest);
119119
builder.route(GraphQlRequestPredicates.graphQlSse(path), sseHandler::handleRequest);
120+
builder.POST(path, this::unsupportedMediaType);
120121
builder.GET(path, this::onlyAllowPost);
121122
if (properties.getGraphiql().isEnabled()) {
122123
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
@@ -129,6 +130,14 @@ public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler h
129130
return builder.build();
130131
}
131132

133+
private ServerResponse unsupportedMediaType(ServerRequest request) {
134+
return ServerResponse.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).headers(this::acceptJson).build();
135+
}
136+
137+
private void acceptJson(HttpHeaders headers) {
138+
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
139+
}
140+
132141
private ServerResponse onlyAllowPost(ServerRequest request) {
133142
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED).headers(this::onlyAllowPost).build();
134143
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,23 @@ void SseSubscriptionShouldWork() {
119119
}
120120

121121
@Test
122-
void httpGetQueryShouldBeSupported() {
122+
void unsupportedContentTypeShouldBeRejected() {
123+
testWithWebClient((client) -> {
124+
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
125+
client.post()
126+
.uri("/graphql")
127+
.contentType(MediaType.TEXT_PLAIN)
128+
.bodyValue("{ \"query\": \"" + query + "\"}")
129+
.exchange()
130+
.expectStatus()
131+
.isEqualTo(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
132+
.expectHeader()
133+
.valueEquals("Accept", "application/json");
134+
});
135+
}
136+
137+
@Test
138+
void httpGetQueryShouldBeRejected() {
123139
testWithWebClient((client) -> {
124140
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
125141
client.get()

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,20 @@ void SseSubscriptionShouldWork() {
111111
}
112112

113113
@Test
114-
void httpGetQueryShouldBeSupported() {
114+
void unsupportedContentTypeShouldBeRejected() {
115+
withMockMvc((mvc) -> {
116+
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
117+
assertThat(mvc.post()
118+
.uri("/graphql")
119+
.content("{\"query\": \"" + query + "\"}")
120+
.contentType(MediaType.TEXT_PLAIN)).hasStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
121+
.headers()
122+
.hasValue("Accept", "application/json");
123+
});
124+
}
125+
126+
@Test
127+
void httpGetQueryShouldBeRejected() {
115128
withMockMvc((mvc) -> {
116129
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
117130
assertThat(mvc.get().uri("/graphql?query={query}", "{\"query\": \"" + query + "\"}"))

0 commit comments

Comments
 (0)