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
This commit is contained in:
@@ -55,6 +55,7 @@ import org.springframework.graphql.server.webflux.SchemaHandler;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
@@ -112,6 +113,7 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
RouterFunctions.Builder builder = RouterFunctions.route();
|
||||
builder.route(GraphQlRequestPredicates.graphQlHttp(path), httpHandler::handleRequest);
|
||||
builder.route(GraphQlRequestPredicates.graphQlSse(path), sseHandler::handleRequest);
|
||||
builder.POST(path, this::unsupportedMediaType);
|
||||
builder.GET(path, this::onlyAllowPost);
|
||||
if (properties.getGraphiql().isEnabled()) {
|
||||
GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
||||
@@ -124,6 +126,14 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> unsupportedMediaType(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).headers(this::acceptJson).build();
|
||||
}
|
||||
|
||||
private void acceptJson(HttpHeaders headers) {
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> onlyAllowPost(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED).headers(this::onlyAllowPost).build();
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
RouterFunctions.Builder builder = RouterFunctions.route();
|
||||
builder.route(GraphQlRequestPredicates.graphQlHttp(path), httpHandler::handleRequest);
|
||||
builder.route(GraphQlRequestPredicates.graphQlSse(path), sseHandler::handleRequest);
|
||||
builder.POST(path, this::unsupportedMediaType);
|
||||
builder.GET(path, this::onlyAllowPost);
|
||||
if (properties.getGraphiql().isEnabled()) {
|
||||
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
||||
@@ -129,6 +130,14 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private ServerResponse unsupportedMediaType(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).headers(this::acceptJson).build();
|
||||
}
|
||||
|
||||
private void acceptJson(HttpHeaders headers) {
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
private ServerResponse onlyAllowPost(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED).headers(this::onlyAllowPost).build();
|
||||
}
|
||||
|
||||
@@ -119,7 +119,23 @@ class GraphQlWebFluxAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpGetQueryShouldBeSupported() {
|
||||
void unsupportedContentTypeShouldBeRejected() {
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
client.post()
|
||||
.uri("/graphql")
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.bodyValue("{ \"query\": \"" + query + "\"}")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
|
||||
.expectHeader()
|
||||
.valueEquals("Accept", "application/json");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpGetQueryShouldBeRejected() {
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
client.get()
|
||||
|
||||
@@ -111,7 +111,20 @@ class GraphQlWebMvcAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpGetQueryShouldBeSupported() {
|
||||
void unsupportedContentTypeShouldBeRejected() {
|
||||
withMockMvc((mvc) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
assertThat(mvc.post()
|
||||
.uri("/graphql")
|
||||
.content("{\"query\": \"" + query + "\"}")
|
||||
.contentType(MediaType.TEXT_PLAIN)).hasStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
|
||||
.headers()
|
||||
.hasValue("Accept", "application/json");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpGetQueryShouldBeRejected() {
|
||||
withMockMvc((mvc) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
assertThat(mvc.get().uri("/graphql?query={query}", "{\"query\": \"" + query + "\"}"))
|
||||
|
||||
Reference in New Issue
Block a user