From 23691a75bca63d7300e5d99f1e1fc8f3ccecf204 Mon Sep 17 00:00:00 2001 From: Marco Kunze Date: Sat, 1 Aug 2026 00:45:40 +0300 Subject: [PATCH] Match path before Content-Type in GraphQlRequestPredicates The GraphQL route predicates evaluated the request path last, after the method, Content-Type and Accept checks. Since the Content-Type check throws UnsupportedMediaTypeStatusException for an unparseable header, any POST anywhere in the application with a malformed Content-Type was answered with 415 during handler lookup, even when the request did not target the GraphQL endpoint at all. Evaluate the path first so that requests to other paths are never affected by Content-Type parsing. The 415 for unparseable Content-Type values on the GraphQL path itself is unchanged. Applies to both the WebMvc and the WebFlux predicate variants. See gh-1485 --- .../server/webflux/GraphQlRequestPredicates.java | 6 +++--- .../server/webmvc/GraphQlRequestPredicates.java | 6 +++--- .../server/webflux/GraphQlRequestPredicatesTests.java | 10 ++++++++++ .../server/webmvc/GraphQlRequestPredicatesTests.java | 9 +++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java index 2fa5fd5a..dd29d051 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java @@ -97,10 +97,10 @@ private static class GraphQlHttpRequestPredicate implements RequestPredicate { @Override public boolean test(ServerRequest request) { - return httpMethodMatch(request, HttpMethod.POST) + return pathMatch(request, this.pattern) + && httpMethodMatch(request, HttpMethod.POST) && contentTypeMatch(request, this.contentTypes) - && acceptMatch(request, this.acceptedMediaTypes) - && pathMatch(request, this.pattern); + && acceptMatch(request, this.acceptedMediaTypes); } private static boolean httpMethodMatch(ServerRequest request, HttpMethod expected) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java index 9b7ad534..0addd7d7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java @@ -97,10 +97,10 @@ private static class GraphQlHttpRequestPredicate implements RequestPredicate { @Override public boolean test(ServerRequest request) { - return httpMethodMatch(request, HttpMethod.POST) + return pathMatch(request, this.pattern) + && httpMethodMatch(request, HttpMethod.POST) && contentTypeMatch(request, this.contentTypes) - && acceptMatch(request, this.acceptedMediaTypes) - && pathMatch(request, this.pattern); + && acceptMatch(request, this.acceptedMediaTypes); } private static boolean httpMethodMatch(ServerRequest request, HttpMethod expected) { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java index 0dd70a28..fb7368a7 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicatesTests.java @@ -105,6 +105,16 @@ void shouldRejectRequestWithIncompatibleContentType() { assertThat(httpPredicate.test(serverRequest)).isFalse(); } + @Test // gh-1485 + void shouldNotRejectRequestWithInvalidContentTypeOnDifferentPath() { + MockServerHttpRequest request = MockServerHttpRequest.post("/invalid") + .header(HttpHeaders.CONTENT_TYPE, "bogus") + .accept(MediaType.APPLICATION_JSON, MediaTypes.APPLICATION_GRAPHQL_RESPONSE) + .build(); + ServerRequest serverRequest = ServerRequest.create(MockServerWebExchange.from(request), Collections.emptyList()); + assertThat(httpPredicate.test(serverRequest)).isFalse(); + } + @Test void shouldRejectRequestWithInvalidContentType() { ServerWebExchange exchange = createMatchingHttpExchange() diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicatesTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicatesTests.java index c1316afb..f313f182 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicatesTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicatesTests.java @@ -104,6 +104,15 @@ void shouldRejectRequestWithInvalidContentType() { assertThatThrownBy(() -> httpPredicate.test(request)).isInstanceOf(UnsupportedMediaTypeStatusException.class); } + @Test // gh-1485 + void shouldNotRejectRequestWithInvalidContentTypeOnDifferentPath() { + MockHttpServletRequest servletRequest = createMatchingHttpRequest(); + servletRequest.setRequestURI("/invalid"); + servletRequest.setContentType("bogus"); + ServerRequest request = ServerRequest.create(servletRequest, List.of()); + assertThat(httpPredicate.test(request)).isFalse(); + } + @Test void shouldRejectRequestWithIncompatibleAccept() { MockHttpServletRequest request = createMatchingHttpRequest();