Skip to content

Commit f640f86

Browse files
committed
Add QUERY HTTP method
1 parent b699b65 commit f640f86

File tree

67 files changed

+752
-71
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+752
-71
lines changed

spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,16 @@ public static BaseBuilder<?> options(String urlTemplate, @Nullable Object... uri
184184
return method(HttpMethod.OPTIONS, urlTemplate, uriVars);
185185
}
186186

187+
/**
188+
* HTTP POST variant. See {@link #get(String, Object...)} for general info.
189+
* @param urlTemplate a URL template; the resulting URL will be encoded
190+
* @param uriVars zero or more URI variables
191+
* @return the created builder
192+
*/
193+
public static BodyBuilder query(String urlTemplate, @Nullable Object... uriVars) {
194+
return method(HttpMethod.QUERY, urlTemplate, uriVars);
195+
}
196+
187197
/**
188198
* Create a builder with the given HTTP method and a {@link URI}.
189199
* @param method the HTTP method (GET, POST, etc)

spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ public RequestHeadersUriSpec<?> options() {
165165
return methodInternal(HttpMethod.OPTIONS);
166166
}
167167

168+
@Override
169+
public RequestBodyUriSpec query() {
170+
return methodInternal(HttpMethod.QUERY);
171+
}
172+
168173
@Override
169174
public RequestBodyUriSpec method(HttpMethod httpMethod) {
170175
return methodInternal(httpMethod);

spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ public interface WebTestClient {
146146
*/
147147
RequestHeadersUriSpec<?> options();
148148

149+
/**
150+
* Prepare an HTTP QUERY request.
151+
* @return a spec for specifying the target URL
152+
*/
153+
RequestBodyUriSpec query();
154+
149155
/**
150156
* Prepare a request for the specified {@code HttpMethod}.
151157
* @return a spec for specifying the target URL

spring-test/src/main/java/org/springframework/test/web/servlet/assertj/MockMvcTester.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,20 @@ public MockMvcRequestBuilder options() {
333333
return method(HttpMethod.OPTIONS);
334334
}
335335

336+
/**
337+
* Prepare an HTTP QUERY request.
338+
* <p>The returned builder can be wrapped in {@code assertThat} to enable
339+
* assertions on the result. For multi-statements assertions, use
340+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
341+
* result. To control the time to wait for asynchronous request to complete
342+
* on a per-request basis, use
343+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
344+
* @return a request builder for specifying the target URI
345+
*/
346+
public MockMvcRequestBuilder query() {
347+
return method(HttpMethod.QUERY);
348+
}
349+
336350
/**
337351
* Prepare a request for the specified {@code HttpMethod}.
338352
* <p>The returned builder can be wrapped in {@code assertThat} to enable

spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ public static MockHttpServletRequestBuilder head(URI uri) {
175175
return new MockHttpServletRequestBuilder(HttpMethod.HEAD).uri(uri);
176176
}
177177

178+
/**
179+
* Create a {@link MockHttpServletRequestBuilder} for a QUERY request.
180+
* @param uriTemplate a URI template; the resulting URI will be encoded
181+
* @param uriVariables zero or more URI variables
182+
*/
183+
public static MockHttpServletRequestBuilder query(String uriTemplate, @Nullable Object... uriVariables) {
184+
return new MockHttpServletRequestBuilder(HttpMethod.QUERY).uri(uriTemplate, uriVariables);
185+
}
186+
187+
/**
188+
* Create a {@link MockHttpServletRequestBuilder} for a QUERY request.
189+
* @param uri the URI
190+
* @since x.x.x
191+
*/
192+
public static MockHttpServletRequestBuilder query(URI uri) {
193+
return new MockHttpServletRequestBuilder(HttpMethod.QUERY).uri(uri);
194+
}
195+
178196
/**
179197
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method.
180198
* @param method the HTTP method (GET, POST, etc.)

spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.assertj.core.api.ThrowingConsumer;
3333
import org.junit.jupiter.api.Test;
3434

35+
import org.junit.jupiter.params.ParameterizedTest;
3536
import org.springframework.http.HttpHeaders;
3637
import org.springframework.http.HttpMethod;
3738
import org.springframework.http.MediaType;
@@ -53,6 +54,7 @@
5354
import static org.assertj.core.api.Assertions.entry;
5455
import static org.springframework.http.HttpMethod.GET;
5556
import static org.springframework.http.HttpMethod.POST;
57+
import static org.springframework.http.HttpMethod.QUERY;
5658

5759
/**
5860
* Tests for building a {@link MockHttpServletRequest} with
@@ -391,17 +393,20 @@ void requestParameterFromMultiValueMap() {
391393
}
392394

393395
@Test
396+
@ParameterizedTest()
394397
void requestParameterFromRequestBodyFormData() {
395398
String contentType = "application/x-www-form-urlencoded;charset=UTF-8";
396399
String body = "name+1=value+1&name+2=value+A&name+2=value+B&name+3";
397400

398-
MockHttpServletRequest request = new MockHttpServletRequestBuilder(POST).uri("/foo")
399-
.contentType(contentType).content(body.getBytes(UTF_8))
400-
.buildRequest(this.servletContext);
401+
for (HttpMethod method : List.of(POST, QUERY)) {
402+
MockHttpServletRequest request = new MockHttpServletRequestBuilder(method).uri("/foo")
403+
.contentType(contentType).content(body.getBytes(UTF_8))
404+
.buildRequest(this.servletContext);
401405

402-
assertThat(request.getParameterMap().get("name 1")).containsExactly("value 1");
403-
assertThat(request.getParameterMap().get("name 2")).containsExactly("value A", "value B");
404-
assertThat(request.getParameterMap().get("name 3")).containsExactly((String) null);
406+
assertThat(request.getParameterMap().get("name 1")).containsExactly("value 1");
407+
assertThat(request.getParameterMap().get("name 2")).containsExactly("value A", "value B");
408+
assertThat(request.getParameterMap().get("name 3")).containsExactly((String) null);
409+
}
405410
}
406411

407412
@Test

spring-web/src/main/java/org/springframework/http/HttpHeaders.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ public class HttpHeaders implements Serializable {
130130
* @see <a href="https://tools.ietf.org/html/rfc7233#section-2.3">Section 5.3.5 of RFC 7233</a>
131131
*/
132132
public static final String ACCEPT_RANGES = "Accept-Ranges";
133+
134+
/**
135+
* The HTTP {@code Accept-Query} header field name.
136+
* @see <a href="https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html">IETF Draft</a>
137+
*/
138+
public static final String ACCEPT_QUERY = "Accept-Query";
133139
/**
134140
* The CORS {@code Access-Control-Allow-Credentials} response header field name.
135141
* @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
@@ -635,6 +641,27 @@ public List<MediaType> getAcceptPatch() {
635641
return MediaType.parseMediaTypes(get(ACCEPT_PATCH));
636642
}
637643

644+
/**
645+
* Set the list of acceptable {@linkplain MediaType media types} for
646+
* {@code QUERY} methods, as specified by the {@code Accept-Query} header.
647+
* @since x.x.x
648+
*/
649+
public void setAcceptQuery(List<MediaType> mediaTypes) {
650+
set(ACCEPT_QUERY, MediaType.toString(mediaTypes));
651+
}
652+
653+
/**
654+
* Return the list of acceptable {@linkplain MediaType media types} for
655+
* {@code QUERY} methods, as specified by the {@code Accept-Query} header.
656+
* <p>Returns an empty list when the acceptable media types are unspecified.
657+
* @since x.x.x
658+
*/
659+
public List<MediaType> getAcceptQuery() {
660+
return MediaType.parseMediaTypes(get(ACCEPT_QUERY));
661+
}
662+
663+
664+
638665
/**
639666
* Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
640667
*/

spring-web/src/main/java/org/springframework/http/HttpMethod.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,25 @@ public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
3737

3838
/**
3939
* The HTTP method {@code GET}.
40-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3">HTTP 1.1, section 9.3</a>
40+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.1">HTTP Semantics, section 9.3.1</a>
4141
*/
4242
public static final HttpMethod GET = new HttpMethod("GET");
4343

4444
/**
4545
* The HTTP method {@code HEAD}.
46-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4">HTTP 1.1, section 9.4</a>
46+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.2">HTTP Semantics, section 9.3.2</a>
4747
*/
4848
public static final HttpMethod HEAD = new HttpMethod("HEAD");
4949

5050
/**
5151
* The HTTP method {@code POST}.
52-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5">HTTP 1.1, section 9.5</a>
52+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3">HTTP Semantics, section 9.3.3</a>
5353
*/
5454
public static final HttpMethod POST = new HttpMethod("POST");
5555

5656
/**
5757
* The HTTP method {@code PUT}.
58-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6">HTTP 1.1, section 9.6</a>
58+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.4">HTTP Semantics, section 9.3.4</a>
5959
*/
6060
public static final HttpMethod PUT = new HttpMethod("PUT");
6161

@@ -67,23 +67,29 @@ public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
6767

6868
/**
6969
* The HTTP method {@code DELETE}.
70-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7">HTTP 1.1, section 9.7</a>
70+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.5">HTTP Semantics, section 9.3.5</a>
7171
*/
7272
public static final HttpMethod DELETE = new HttpMethod("DELETE");
7373

7474
/**
7575
* The HTTP method {@code OPTIONS}.
76-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2">HTTP 1.1, section 9.2</a>
76+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.7">HTTP Semantics, section 9.3.7</a>
7777
*/
7878
public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS");
7979

8080
/**
8181
* The HTTP method {@code TRACE}.
82-
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8">HTTP 1.1, section 9.8</a>
82+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.8">HTTP Semantics, section 9.3.8</a>
8383
*/
8484
public static final HttpMethod TRACE = new HttpMethod("TRACE");
8585

86-
private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE };
86+
/**
87+
* The HTTP method {@code QUERY}.
88+
* @see <a href="https://httpwg.org/http-extensions/draft-ietf-httpbis-safe-method-w-body.html">IETF Draft</a>
89+
*/
90+
public static final HttpMethod QUERY = new HttpMethod("QUERY");
91+
92+
private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, QUERY };
8793

8894

8995
private final String name;
@@ -97,7 +103,7 @@ private HttpMethod(String name) {
97103
* Returns an array containing the standard HTTP methods. Specifically,
98104
* this method returns an array containing {@link #GET}, {@link #HEAD},
99105
* {@link #POST}, {@link #PUT}, {@link #PATCH}, {@link #DELETE},
100-
* {@link #OPTIONS}, and {@link #TRACE}.
106+
* {@link #OPTIONS}, {@link #TRACE}, and {@link #QUERY}.
101107
*
102108
* <p>Note that the returned value does not include any HTTP methods defined
103109
* in WebDav.
@@ -124,6 +130,7 @@ public static HttpMethod valueOf(String method) {
124130
case "DELETE" -> DELETE;
125131
case "OPTIONS" -> OPTIONS;
126132
case "TRACE" -> TRACE;
133+
case "QUERY" -> QUERY;
127134
default -> new HttpMethod(method);
128135
};
129136
}

spring-web/src/main/java/org/springframework/http/RequestEntity.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,26 @@ public static BodyBuilder post(String uriTemplate, @Nullable Object... uriVariab
382382
return method(HttpMethod.POST, uriTemplate, uriVariables);
383383
}
384384

385+
/**
386+
* Create an HTTP QUERY builder with the given url.
387+
* @param url the URL
388+
* @return the created builder
389+
*/
390+
public static BodyBuilder query(URI url) {
391+
return method(HttpMethod.QUERY, url);
392+
}
393+
394+
/**
395+
* Create an HTTP QUERY builder with the given string base uri template.
396+
* @param uriTemplate the uri template to use
397+
* @param uriVariables variables to expand the URI template with
398+
* @return the created builder
399+
* @since x.x.x
400+
*/
401+
public static BodyBuilder query(String uriTemplate, Object... uriVariables) {
402+
return method(HttpMethod.QUERY, uriTemplate, uriVariables);
403+
}
404+
385405
/**
386406
* Create an HTTP PUT builder with the given url.
387407
* @param url the URL

spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.hc.client5.http.classic.methods.HttpPost;
3333
import org.apache.hc.client5.http.classic.methods.HttpPut;
3434
import org.apache.hc.client5.http.classic.methods.HttpTrace;
35+
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
3536
import org.apache.hc.client5.http.config.Configurable;
3637
import org.apache.hc.client5.http.config.RequestConfig;
3738
import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -345,6 +346,9 @@ else if (HttpMethod.OPTIONS.equals(httpMethod)) {
345346
else if (HttpMethod.TRACE.equals(httpMethod)) {
346347
return new HttpTrace(uri);
347348
}
349+
else if (HttpMethod.QUERY.equals(httpMethod)) {
350+
return new HttpUriRequestBase(HttpMethod.QUERY.name(), uri);
351+
}
348352
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
349353
}
350354

spring-web/src/main/java/org/springframework/web/HttpMediaTypeNotSupportedException.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ public HttpHeaders getHeaders() {
132132
if (HttpMethod.PATCH.equals(this.httpMethod)) {
133133
headers.setAcceptPatch(getSupportedMediaTypes());
134134
}
135+
if (HttpMethod.QUERY.equals(this.httpMethod)) {
136+
headers.setAcceptQuery(getSupportedMediaTypes());
137+
}
135138
return headers;
136139
}
137140

spring-web/src/main/java/org/springframework/web/bind/annotation/DeleteMapping.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @see PostMapping
4545
* @see PutMapping
4646
* @see PatchMapping
47+
* @see QueryMapping
4748
* @see RequestMapping
4849
*/
4950
@Target(ElementType.METHOD)

spring-web/src/main/java/org/springframework/web/bind/annotation/GetMapping.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @see PutMapping
4545
* @see DeleteMapping
4646
* @see PatchMapping
47+
* @see QueryMapping
4748
* @see RequestMapping
4849
*/
4950
@Target(ElementType.METHOD)

spring-web/src/main/java/org/springframework/web/bind/annotation/PatchMapping.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @see PostMapping
4545
* @see PutMapping
4646
* @see DeleteMapping
47+
* @see QueryMapping
4748
* @see RequestMapping
4849
*/
4950
@Target(ElementType.METHOD)

spring-web/src/main/java/org/springframework/web/bind/annotation/PostMapping.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @see PutMapping
4545
* @see DeleteMapping
4646
* @see PatchMapping
47+
* @see QueryMapping
4748
* @see RequestMapping
4849
*/
4950
@Target(ElementType.METHOD)

spring-web/src/main/java/org/springframework/web/bind/annotation/PutMapping.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @see PostMapping
4545
* @see DeleteMapping
4646
* @see PatchMapping
47+
* @see QueryMapping
4748
* @see RequestMapping
4849
*/
4950
@Target(ElementType.METHOD)

0 commit comments

Comments
 (0)