diff --git a/spring-web/src/main/java/org/springframework/http/HttpMethod.java b/spring-web/src/main/java/org/springframework/http/HttpMethod.java index 422abf282031..2f4b287f0b14 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpMethod.java +++ b/spring-web/src/main/java/org/springframework/http/HttpMethod.java @@ -73,6 +73,12 @@ public final class HttpMethod implements Comparable, Serializable { */ public static final HttpMethod DELETE = new HttpMethod("DELETE"); + /** + * The HTTP method {@code QUERY}. + * @see RFC 10008 + */ + public static final HttpMethod QUERY = new HttpMethod("QUERY"); + /** * The HTTP method {@code OPTIONS}. * @see HTTP 1.1, section 9.2 @@ -85,7 +91,7 @@ public final class HttpMethod implements Comparable, Serializable { */ public static final HttpMethod TRACE = new HttpMethod("TRACE"); - private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }; + private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, QUERY, OPTIONS, TRACE }; private final String name; @@ -98,7 +104,7 @@ private HttpMethod(String name) { /** * Returns an array containing the standard HTTP methods. Specifically, * this method returns an array containing {@link #GET}, {@link #HEAD}, - * {@link #POST}, {@link #PUT}, {@link #PATCH}, {@link #DELETE}, + * {@link #POST}, {@link #PUT}, {@link #PATCH}, {@link #DELETE}, {@link #QUERY}, * {@link #OPTIONS}, and {@link #TRACE}. * *

Note that the returned value does not include any HTTP methods defined @@ -135,6 +141,7 @@ public static HttpMethod valueOf(String method) { case "PUT" -> PUT; case "PATCH" -> PATCH; case "DELETE" -> DELETE; + case "QUERY" -> QUERY; case "OPTIONS" -> OPTIONS; case "TRACE" -> TRACE; default -> new HttpMethod(method); diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java index cc7ebcc776c9..47b02293406a 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java @@ -32,6 +32,7 @@ import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpPut; import org.apache.hc.client5.http.classic.methods.HttpTrace; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.HttpClients; @@ -294,6 +295,9 @@ else if (HttpMethod.PATCH.equals(httpMethod)) { else if (HttpMethod.DELETE.equals(httpMethod)) { return new HttpDelete(uri); } + else if (HttpMethod.QUERY.equals(httpMethod)) { + return new HttpUriRequestBase(httpMethod.name(), uri); + } else if (HttpMethod.OPTIONS.equals(httpMethod)) { return new HttpOptions(uri); } diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index 07f994561780..8a837b8a0711 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -125,7 +125,7 @@ /** * The HTTP request methods to map to, narrowing the primary mapping: - * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. + * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, QUERY, TRACE. *

Supported at the type level as well as at the method level! * When used at the type level, all method-level mappings inherit this * HTTP method restriction. diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java index c4d353612080..ee77ee8b2de6 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java @@ -26,10 +26,11 @@ * {@link RequestMapping#method()} attribute of the {@link RequestMapping} annotation. * *

Note that, by default, {@link org.springframework.web.servlet.DispatcherServlet} - * supports GET, HEAD, POST, PUT, PATCH, and DELETE only. DispatcherServlet will - * process TRACE and OPTIONS with the default HttpServlet behavior unless explicitly - * told to dispatch those request types as well: Check out the "dispatchOptionsRequest" - * and "dispatchTraceRequest" properties, switching them to "true" if necessary. + * supports GET, HEAD, POST, PUT, PATCH, DELETE, and custom methods such as QUERY. + * DispatcherServlet will process TRACE and OPTIONS with the default HttpServlet + * behavior unless explicitly told to dispatch those request types as well: + * check out the "dispatchOptionsRequest" and "dispatchTraceRequest" properties, + * switching them to "true" if necessary. * * @author Juergen Hoeller * @since 2.5 @@ -39,7 +40,7 @@ */ public enum RequestMethod { - GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + GET, HEAD, POST, PUT, PATCH, DELETE, QUERY, OPTIONS, TRACE; /** @@ -58,6 +59,7 @@ public enum RequestMethod { case "PUT" -> PUT; case "PATCH" -> PATCH; case "DELETE" -> DELETE; + case "QUERY" -> QUERY; case "OPTIONS" -> OPTIONS; case "TRACE" -> TRACE; default -> null; @@ -90,6 +92,7 @@ public HttpMethod asHttpMethod() { case PUT -> HttpMethod.PUT; case PATCH -> HttpMethod.PATCH; case DELETE -> HttpMethod.DELETE; + case QUERY -> HttpMethod.QUERY; case OPTIONS -> HttpMethod.OPTIONS; case TRACE -> HttpMethod.TRACE; }; diff --git a/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java b/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java index ac3a3f0ae4e6..3c3d15ab04f6 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java @@ -44,12 +44,12 @@ void comparison() { void values() { HttpMethod[] values = HttpMethod.values(); assertThat(values).containsExactly(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.POST, HttpMethod.PUT, - HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.OPTIONS, HttpMethod.TRACE); + HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.QUERY, HttpMethod.OPTIONS, HttpMethod.TRACE); // check defensive copy values[0] = HttpMethod.POST; assertThat(HttpMethod.values()).containsExactly(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.POST, HttpMethod.PUT, - HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.OPTIONS, HttpMethod.TRACE); + HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.QUERY, HttpMethod.OPTIONS, HttpMethod.TRACE); } @Test diff --git a/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java b/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java index f9c2c59d256e..ae8afbb7c4f5 100644 --- a/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java @@ -61,6 +61,7 @@ protected ClientHttpRequestFactory createRequestFactory() { void httpMethods() throws Exception { super.httpMethods(); assertHttpMethod("patch", HttpMethod.PATCH); + assertHttpMethod("query", HttpMethod.QUERY); } @Test @@ -186,7 +187,7 @@ void shouldSetContentLengthWhenEmptyBody(HttpMethod method) throws Exception { } static Stream unsafeHttpMethods() { - return Stream.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH); + return Stream.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH, HttpMethod.QUERY); } @ParameterizedTest diff --git a/spring-web/src/test/java/org/springframework/web/bind/annotation/RequestMethodTests.java b/spring-web/src/test/java/org/springframework/web/bind/annotation/RequestMethodTests.java index a16fb2b84d70..c6356c83b4a9 100644 --- a/spring-web/src/test/java/org/springframework/web/bind/annotation/RequestMethodTests.java +++ b/spring-web/src/test/java/org/springframework/web/bind/annotation/RequestMethodTests.java @@ -29,7 +29,7 @@ class RequestMethodTests { @Test void resolveString() { - String[] methods = new String[]{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "TRACE"}; + String[] methods = new String[]{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "QUERY", "OPTIONS", "TRACE"}; for (String httpMethod : methods) { RequestMethod requestMethod = RequestMethod.resolve(httpMethod); assertThat(requestMethod).isNotNull(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index 8ca35f451ff2..dcada8428d2a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -193,7 +193,7 @@ void getHandlerMediaTypeNotSupportedWithParseError(TestRequestMappingInfoHandler void getHandlerHttpOptions(TestRequestMappingInfoHandlerMapping mapping) throws Exception { testHttpOptions(mapping, "/foo", "GET,HEAD,OPTIONS", null); testHttpOptions(mapping, "/person/1", "PUT,OPTIONS", null); - testHttpOptions(mapping, "/persons", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS", null); + testHttpOptions(mapping, "/persons", "GET,HEAD,POST,PUT,PATCH,DELETE,QUERY,OPTIONS", null); testHttpOptions(mapping, "/something", "PUT,POST", null); testHttpOptions(mapping, "/qux", "PATCH,GET,HEAD,OPTIONS", new MediaType("foo", "bar")); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/WebContentGeneratorTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/WebContentGeneratorTests.java index 4970cf998f58..371121bfe626 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/WebContentGeneratorTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/WebContentGeneratorTests.java @@ -39,7 +39,7 @@ void getAllowHeaderWithConstructorTrue() { @Test void getAllowHeaderWithConstructorFalse() { WebContentGenerator generator = new TestWebContentGenerator(false); - assertThat(generator.getAllowHeader()).isEqualTo("GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"); + assertThat(generator.getAllowHeader()).isEqualTo("GET,HEAD,POST,PUT,PATCH,DELETE,QUERY,OPTIONS"); } @Test @@ -59,7 +59,8 @@ void getAllowHeaderWithSupportedMethodsSetter() { void getAllowHeaderWithSupportedMethodsSetterEmpty() { WebContentGenerator generator = new TestWebContentGenerator(); generator.setSupportedMethods(); - assertThat(generator.getAllowHeader()).as("Effectively \"no restriction\" on supported methods").isEqualTo("GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"); + assertThat(generator.getAllowHeader()).as("Effectively \"no restriction\" on supported methods") + .isEqualTo("GET,HEAD,POST,PUT,PATCH,DELETE,QUERY,OPTIONS"); } @Test