Return 405 "Allow:POST" for HTTP GET queries

See gh-70
This commit is contained in:
Rossen Stoyanchev
2021-07-01 15:48:55 +01:00
parent e3be107f96
commit 9178075da5
4 changed files with 65 additions and 6 deletions

View File

@@ -42,6 +42,8 @@ import org.springframework.graphql.web.WebGraphQlHandler;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.graphql.web.webflux.GraphQlHttpHandler;
import org.springframework.graphql.web.webflux.GraphQlWebSocketHandler;
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.reactive.HandlerMapping;
@@ -87,18 +89,27 @@ public class GraphQlWebFluxAutoConfiguration {
@Bean
public RouterFunction<ServerResponse> graphQlEndpoint(GraphQlHttpHandler handler, GraphQlSource graphQlSource,
GraphQlProperties properties, ResourceLoader resourceLoader) {
String graphQLPath = properties.getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint HTTP POST " + graphQLPath);
}
// @formatter:off
RouterFunctions.Builder builder = RouterFunctions.route()
.POST(graphQLPath, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest);
.GET(graphQLPath, request ->
ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
.headers(headers -> headers.setAllow(Collections.singleton(HttpMethod.POST)))
.build())
.POST(graphQLPath,
accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)),
handler::handleRequest);
if (properties.getGraphiql().isEnabled()) {
Resource resource = resourceLoader.getResource("classpath:graphiql/index.html");
GraphiQlWebFluxHandler graphiQlHandler = new GraphiQlWebFluxHandler(graphQLPath, resource);
builder = builder.GET(properties.getGraphiql().getPath(), graphiQlHandler::showGraphiQlPage);
}
if (properties.getSchema().getPrinter().isEnabled()) {
SchemaPrinter printer = new SchemaPrinter();
builder = builder.GET(graphQLPath + properties.getSchema().getPrinter().getPath(),

View File

@@ -47,6 +47,8 @@ import org.springframework.graphql.web.WebGraphQlHandler;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.graphql.web.webmvc.GraphQlHttpHandler;
import org.springframework.graphql.web.webmvc.GraphQlWebSocketHandler;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.HandlerMapping;
@@ -94,20 +96,30 @@ public class GraphQlWebMvcAutoConfiguration {
}
@Bean
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler handler, GraphQlSource graphQlSource,
GraphQlProperties properties, ResourceLoader resourceLoader) {
public RouterFunction<ServerResponse> graphQlRouterFunction(GraphQlHttpHandler handler,
GraphQlSource graphQlSource, GraphQlProperties properties, ResourceLoader resourceLoader) {
String graphQLPath = properties.getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint HTTP POST " + graphQLPath);
}
// @formatter:off
RouterFunctions.Builder builder = RouterFunctions.route()
.POST(graphQLPath, contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)), handler::handleRequest);
.GET(graphQLPath, request ->
ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
.headers(headers -> headers.setAllow(Collections.singleton(HttpMethod.POST)))
.build())
.POST(graphQLPath,
contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)),
handler::handleRequest);
if (properties.getGraphiql().isEnabled()) {
Resource resource = resourceLoader.getResource("classpath:graphiql/index.html");
GraphiQlWebMvcHandler graphiQLHandler = new GraphiQlWebMvcHandler(graphQLPath, resource);
builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::showGraphiQlPage);
}
if (properties.getSchema().getPrinter().isEnabled()) {
SchemaPrinter printer = new SchemaPrinter();
builder = builder.GET(graphQLPath + properties.getSchema().getPrinter().getPath(),

View File

@@ -31,6 +31,7 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@@ -63,7 +64,6 @@ class GraphQlWebFluxAutoConfigurationTests {
" author" +
" }" +
"}";
client.post().uri("").bodyValue("{ \"query\": \"" + query + "\"}")
.exchange()
.expectStatus()
@@ -74,6 +74,25 @@ class GraphQlWebFluxAutoConfigurationTests {
});
}
@Test
void queryHttpGet() {
testWithWebClient((client) -> {
String query = "{" +
" bookById(id: \\\"book-1\\\"){ " +
" id" +
" name" +
" pageCount" +
" author" +
" }" +
"}";
client.get().uri("?query={query}", "{ \"query\": \"" + query + "\"}")
.exchange()
.expectStatus()
.isEqualTo(HttpStatus.METHOD_NOT_ALLOWED)
.expectHeader().valueEquals("Allow", "POST");
});
}
@Test
void queryMissing() {
testWithWebClient((client) ->

View File

@@ -58,7 +58,7 @@ class GraphQlWebMvcAutoConfigurationTests {
"spring.graphql.schema.locations=classpath:books/");
@Test
void endpointHandlesGraphQlQuery() {
void query() {
testWith((mockMvc) -> {
String query = "{" +
" bookById(id: \\\"book-1\\\"){ " +
@@ -76,6 +76,23 @@ class GraphQlWebMvcAutoConfigurationTests {
});
}
@Test
void queryHttpGet() {
testWith((mockMvc) -> {
String query = "{" +
" bookById(id: \\\"book-1\\\"){ " +
" id" +
" name" +
" pageCount" +
" author" +
" }" +
"}";
mockMvc.perform(get("/graphql?query={query}", "{\"query\": \"" + query + "\"}"))
.andExpect(status().isMethodNotAllowed())
.andExpect(header().string("Allow", "POST"));
});
}
@Test
void missingQuery() {
testWith((mockMvc) -> mockMvc.perform(post("/graphql").content("{}")).andExpect(status().isBadRequest()));