mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Add QUERY HTTP method
Signed-off-by: Mario Daniel Ruiz Saavedra <desiderantes93@gmail.com>
This commit is contained in:
committed by
Brian Clozel
parent
555ac3768d
commit
4a64537ac6
+1
-1
@@ -118,7 +118,7 @@ class RestTestClientTests {
|
||||
RestTestClientTests.this.client.options().uri("/test")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().valueEquals("Allow", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS")
|
||||
.expectHeader().valueEquals("Allow", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,QUERY")
|
||||
.expectBody().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -31,6 +31,8 @@ import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import org.assertj.core.api.ThrowingConsumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -418,18 +420,21 @@ class MockHttpServletRequestBuilderTests {
|
||||
assertThat(request.getParameterMap().get("foo")).containsExactly("bar", "baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestParameterFromRequestBodyFormData() {
|
||||
@ValueSource(strings = {"POST", "QUERY"})
|
||||
@ParameterizedTest()
|
||||
void requestParameterFromRequestBodyFormData(String methodName) {
|
||||
String contentType = "application/x-www-form-urlencoded;charset=UTF-8";
|
||||
String body = "name+1=value+1&name+2=value+A&name+2=value+B&name+3";
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequestBuilder(POST).uri("/foo")
|
||||
HttpMethod method = HttpMethod.valueOf(methodName);
|
||||
MockHttpServletRequest request = new MockHttpServletRequestBuilder(method).uri("/foo")
|
||||
.contentType(contentType).content(body.getBytes(UTF_8))
|
||||
.buildRequest(this.servletContext);
|
||||
|
||||
assertThat(request.getParameterMap().get("name 1")).containsExactly("value 1");
|
||||
assertThat(request.getParameterMap().get("name 2")).containsExactly("value A", "value B");
|
||||
assertThat(request.getParameterMap().get("name 3")).containsExactly((String) null);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -129,6 +129,13 @@ public class HttpHeaders implements Serializable {
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7233#section-2.3">Section 5.3.5 of RFC 7233</a>
|
||||
*/
|
||||
public static final String ACCEPT_RANGES = "Accept-Ranges";
|
||||
|
||||
/**
|
||||
* The HTTP {@code Accept-Query} header field name.
|
||||
* @since 7.1
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc10008.html#section-3">Section 3 of RFC 10008</a>
|
||||
*/
|
||||
public static final String ACCEPT_QUERY = "Accept-Query";
|
||||
/**
|
||||
* The CORS {@code Access-Control-Allow-Credentials} response header field name.
|
||||
* @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
|
||||
@@ -648,6 +655,27 @@ public class HttpHeaders implements Serializable {
|
||||
return MediaType.parseMediaTypes(get(ACCEPT_PATCH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain MediaType media types} for
|
||||
* {@code QUERY} methods, as specified by the {@code Accept-Query} header.
|
||||
* @since 7.1
|
||||
*/
|
||||
public void setAcceptQuery(List<MediaType> mediaTypes) {
|
||||
set(ACCEPT_QUERY, MediaType.toString(mediaTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of acceptable {@linkplain MediaType media types} for
|
||||
* {@code QUERY} methods, as specified by the {@code Accept-Query} header.
|
||||
* <p>Returns an empty list when the acceptable media types are unspecified.
|
||||
* @since 7.1
|
||||
*/
|
||||
public List<MediaType> getAcceptQuery() {
|
||||
return MediaType.parseMediaTypes(get(ACCEPT_QUERY));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
|
||||
*/
|
||||
|
||||
@@ -39,25 +39,25 @@ public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
|
||||
|
||||
/**
|
||||
* The HTTP method {@code GET}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3">HTTP 1.1, section 9.3</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.1">HTTP Semantics, section 9.3.1</a>
|
||||
*/
|
||||
public static final HttpMethod GET = new HttpMethod("GET");
|
||||
|
||||
/**
|
||||
* The HTTP method {@code HEAD}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4">HTTP 1.1, section 9.4</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.2">HTTP Semantics, section 9.3.2</a>
|
||||
*/
|
||||
public static final HttpMethod HEAD = new HttpMethod("HEAD");
|
||||
|
||||
/**
|
||||
* The HTTP method {@code POST}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5">HTTP 1.1, section 9.5</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3">HTTP Semantics, section 9.3.3</a>
|
||||
*/
|
||||
public static final HttpMethod POST = new HttpMethod("POST");
|
||||
|
||||
/**
|
||||
* The HTTP method {@code PUT}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6">HTTP 1.1, section 9.6</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.4">HTTP Semantics, section 9.3.4</a>
|
||||
*/
|
||||
public static final HttpMethod PUT = new HttpMethod("PUT");
|
||||
|
||||
@@ -69,23 +69,30 @@ public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
|
||||
|
||||
/**
|
||||
* The HTTP method {@code DELETE}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7">HTTP 1.1, section 9.7</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.5">HTTP Semantics, section 9.3.5</a>
|
||||
*/
|
||||
public static final HttpMethod DELETE = new HttpMethod("DELETE");
|
||||
|
||||
/**
|
||||
* The HTTP method {@code OPTIONS}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2">HTTP 1.1, section 9.2</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.7">HTTP Semantics, section 9.3.7</a>
|
||||
*/
|
||||
public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS");
|
||||
|
||||
/**
|
||||
* The HTTP method {@code TRACE}.
|
||||
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8">HTTP 1.1, section 9.8</a>
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.8">HTTP Semantics, section 9.3.8</a>
|
||||
*/
|
||||
public static final HttpMethod TRACE = new HttpMethod("TRACE");
|
||||
|
||||
private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE };
|
||||
/**
|
||||
* The HTTP method {@code QUERY}.
|
||||
* @since 7.1
|
||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc10008.html#section-2">The HTTP QUERY Method, section 2</a>
|
||||
*/
|
||||
public static final HttpMethod QUERY = new HttpMethod("QUERY");
|
||||
|
||||
private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, QUERY };
|
||||
|
||||
|
||||
private final String name;
|
||||
@@ -99,7 +106,7 @@ public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
|
||||
* 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 #OPTIONS}, and {@link #TRACE}.
|
||||
* {@link #OPTIONS}, {@link #TRACE}, and {@link #QUERY}.
|
||||
*
|
||||
* <p>Note that the returned value does not include any HTTP methods defined
|
||||
* in WebDav.
|
||||
@@ -137,6 +144,7 @@ public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
|
||||
case "DELETE" -> DELETE;
|
||||
case "OPTIONS" -> OPTIONS;
|
||||
case "TRACE" -> TRACE;
|
||||
case "QUERY" -> QUERY;
|
||||
default -> new HttpMethod(method);
|
||||
};
|
||||
}
|
||||
|
||||
+4
@@ -32,6 +32,7 @@ import org.apache.hc.client5.http.classic.methods.HttpPatch;
|
||||
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;
|
||||
@@ -300,6 +301,9 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
|
||||
else if (HttpMethod.TRACE.equals(httpMethod)) {
|
||||
return new HttpTrace(uri);
|
||||
}
|
||||
else if (HttpMethod.QUERY.equals(httpMethod)) {
|
||||
return new HttpUriRequestBase(HttpMethod.QUERY.name(), uri);
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -132,6 +132,9 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
|
||||
if (HttpMethod.PATCH.equals(this.httpMethod)) {
|
||||
headers.setAcceptPatch(getSupportedMediaTypes());
|
||||
}
|
||||
if (HttpMethod.QUERY.equals(this.httpMethod)) {
|
||||
headers.setAcceptQuery(getSupportedMediaTypes());
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.util.Assert;
|
||||
* {@link RequestMapping#method()} attribute of the {@link RequestMapping} annotation.
|
||||
*
|
||||
* <p>Note that, by default, {@link org.springframework.web.servlet.DispatcherServlet}
|
||||
* supports GET, HEAD, POST, PUT, PATCH, and DELETE only. DispatcherServlet will
|
||||
* supports GET, QUERY, 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.
|
||||
@@ -39,7 +39,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public enum RequestMethod {
|
||||
|
||||
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
|
||||
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, QUERY;
|
||||
|
||||
|
||||
/**
|
||||
@@ -60,6 +60,7 @@ public enum RequestMethod {
|
||||
case "DELETE" -> DELETE;
|
||||
case "OPTIONS" -> OPTIONS;
|
||||
case "TRACE" -> TRACE;
|
||||
case "QUERY" -> QUERY;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
@@ -92,6 +93,7 @@ public enum RequestMethod {
|
||||
case DELETE -> HttpMethod.DELETE;
|
||||
case OPTIONS -> HttpMethod.OPTIONS;
|
||||
case TRACE -> HttpMethod.TRACE;
|
||||
case QUERY -> HttpMethod.QUERY;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@code Filter} that parses form data for HTTP PUT, PATCH, and DELETE requests
|
||||
* {@code Filter} that parses form data for HTTP PUT, PATCH, DELETE, and QUERY requests
|
||||
* and exposes it as Servlet request parameters. By default, the Servlet spec
|
||||
* only requires this for HTTP POST.
|
||||
*
|
||||
@@ -56,7 +56,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class FormContentFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final List<String> HTTP_METHODS = Arrays.asList("PUT", "PATCH", "DELETE");
|
||||
private static final List<String> HTTP_METHODS = Arrays.asList("PUT", "PATCH", "DELETE", "QUERY");
|
||||
|
||||
private FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.springframework.web.util.WebUtils;
|
||||
* is to use a normal POST with an additional hidden form field ({@code _method})
|
||||
* to pass the "real" HTTP method along. This filter reads that parameter and changes
|
||||
* the {@link HttpServletRequestWrapper#getMethod()} return value accordingly.
|
||||
* Only {@code "PUT"}, {@code "DELETE"} and {@code "PATCH"} HTTP methods are allowed.
|
||||
* Only {@code "PUT"}, {@code "DELETE"}, {@code "PATCH"}, and {@code "QUERY"} HTTP methods are allowed.
|
||||
*
|
||||
* <p>The name of the request parameter defaults to {@code _method}, but can be
|
||||
* adapted via the {@link #setMethodParam(String) methodParam} property.
|
||||
@@ -55,7 +55,7 @@ import org.springframework.web.util.WebUtils;
|
||||
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final List<String> ALLOWED_METHODS =
|
||||
List.of(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name());
|
||||
List.of(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name(), HttpMethod.QUERY.name());
|
||||
|
||||
/** Default method parameter: {@code _method}. */
|
||||
public static final String DEFAULT_METHOD_PARAM = "_method";
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ import org.springframework.web.server.WebFilterChain;
|
||||
public class HiddenHttpMethodFilter implements WebFilter {
|
||||
|
||||
private static final List<HttpMethod> ALLOWED_METHODS =
|
||||
List.of(HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH);
|
||||
List.of(HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH, HttpMethod.QUERY);
|
||||
|
||||
/** Default name of the form parameter with the HTTP method to use. */
|
||||
public static final String DEFAULT_METHOD_PARAMETER_NAME = "_method";
|
||||
|
||||
+3
@@ -161,6 +161,9 @@ public class UnsupportedMediaTypeStatusException extends ResponseStatusException
|
||||
if (this.method == HttpMethod.PATCH) {
|
||||
headers.setAcceptPatch(this.supportedMediaTypes);
|
||||
}
|
||||
if (this.method == HttpMethod.QUERY) {
|
||||
headers.setAcceptQuery(this.supportedMediaTypes);
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ class HttpMethodTests {
|
||||
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.OPTIONS, HttpMethod.TRACE, HttpMethod.QUERY);
|
||||
|
||||
// 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.OPTIONS, HttpMethod.TRACE, HttpMethod.QUERY);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -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", "OPTIONS", "TRACE", "QUERY"};
|
||||
for (String httpMethod : methods) {
|
||||
RequestMethod requestMethod = RequestMethod.resolve(httpMethod);
|
||||
assertThat(requestMethod).isNotNull();
|
||||
|
||||
+27
-1
@@ -75,7 +75,7 @@ abstract class AbstractMockWebServerTests {
|
||||
|
||||
private MockResponse getRequest(RecordedRequest request, byte[] body, @Nullable String contentType) {
|
||||
if (request.getMethod().equals("OPTIONS")) {
|
||||
return new MockResponse.Builder().code(200).setHeader("Allow", "GET, OPTIONS, HEAD, TRACE").build();
|
||||
return new MockResponse.Builder().code(200).setHeader("Allow", "GET, QUERY, OPTIONS, HEAD, TRACE").build();
|
||||
}
|
||||
Buffer buf = new Buffer();
|
||||
buf.write(body);
|
||||
@@ -240,6 +240,29 @@ abstract class AbstractMockWebServerTests {
|
||||
return new MockResponse.Builder().code(202).build();
|
||||
}
|
||||
|
||||
private MockResponse queryRequest(RecordedRequest request, String expectedRequestContent,
|
||||
String contentType, byte[] responseBody) {
|
||||
|
||||
assertThat(request.getHeaders().values(CONTENT_LENGTH)).hasSize(1);
|
||||
assertThat(Integer.parseInt(request.getHeaders().get(CONTENT_LENGTH))).as("Invalid request content-length").isGreaterThan(0);
|
||||
String requestContentType = request.getHeaders().get(CONTENT_TYPE);
|
||||
assertThat(requestContentType).as("No content-type").isNotNull();
|
||||
Charset charset = StandardCharsets.ISO_8859_1;
|
||||
if (requestContentType.contains("charset=")) {
|
||||
String charsetName = requestContentType.split("charset=")[1];
|
||||
charset = Charset.forName(charsetName);
|
||||
}
|
||||
assertThat(request.getBody().string(charset)).as("Invalid request body").isEqualTo(expectedRequestContent);
|
||||
Buffer buf = new Buffer();
|
||||
buf.write(responseBody);
|
||||
return new MockResponse.Builder()
|
||||
.code(200)
|
||||
.setHeader(CONTENT_TYPE, contentType)
|
||||
.setHeader(CONTENT_LENGTH, responseBody.length)
|
||||
.body(buf)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
protected class TestDispatcher extends Dispatcher {
|
||||
|
||||
@@ -302,6 +325,9 @@ abstract class AbstractMockWebServerTests {
|
||||
else if (request.getTarget().equals("/put")) {
|
||||
return putRequest(request, helloWorld);
|
||||
}
|
||||
else if (request.getTarget().equals("/query")) {
|
||||
return queryRequest(request, helloWorld, textContentType.toString(), helloWorldBytes);
|
||||
}
|
||||
return new MockResponse.Builder().code(404).build();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests {
|
||||
setUpClient(clientHttpRequestFactory);
|
||||
|
||||
Set<HttpMethod> allowed = template.optionsForAllow(URI.create(baseUrl + "/get"));
|
||||
assertThat(allowed).as("Invalid response").isEqualTo(Set.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE));
|
||||
assertThat(allowed).as("Invalid response").isEqualTo(Set.of(HttpMethod.GET, HttpMethod.QUERY, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE));
|
||||
}
|
||||
|
||||
@ParameterizedRestTemplateTest
|
||||
|
||||
@@ -58,14 +58,14 @@ class FormContentFilterTests {
|
||||
|
||||
|
||||
@Test
|
||||
void wrapPutPatchAndDeleteOnly() throws Exception {
|
||||
void wrapPutPatchQueryAndDeleteOnly() throws Exception {
|
||||
for (HttpMethod method : HttpMethod.values()) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method.name(), "/");
|
||||
request.setContent("foo=bar".getBytes(StandardCharsets.ISO_8859_1));
|
||||
request.setContentType("application/x-www-form-urlencoded; charset=ISO-8859-1");
|
||||
this.filterChain = new MockFilterChain();
|
||||
this.filter.doFilter(request, this.response, this.filterChain);
|
||||
if (method == HttpMethod.PUT || method == HttpMethod.PATCH || method == HttpMethod.DELETE) {
|
||||
if (method == HttpMethod.PUT || method == HttpMethod.PATCH || method == HttpMethod.DELETE || method == HttpMethod.QUERY) {
|
||||
assertThat(this.filterChain.getRequest()).isNotSameAs(request);
|
||||
}
|
||||
else {
|
||||
|
||||
+4
@@ -113,6 +113,10 @@ public class MvcAnnotationPredicates {
|
||||
return new RequestMappingPredicate(path).method(RequestMethod.HEAD);
|
||||
}
|
||||
|
||||
public static RequestMappingPredicate queryMapping(String... path) {
|
||||
return new RequestMappingPredicate(path).method(RequestMethod.QUERY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class ModelAttributePredicate implements Predicate<MethodParameter> {
|
||||
|
||||
+49
-12
@@ -65,9 +65,12 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
|
||||
private static final Method HTTP_OPTIONS_HANDLE_METHOD;
|
||||
|
||||
private static final Method HTTP_HEAD_QUERY_HANDLE_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
HTTP_OPTIONS_HANDLE_METHOD = HttpOptionsHandler.class.getMethod("handle");
|
||||
HTTP_HEAD_QUERY_HANDLE_METHOD = HttpHeadQueryHandler.class.getMethod("handle");
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Should never happen
|
||||
@@ -189,10 +192,16 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
HttpMethod httpMethod = request.getMethod();
|
||||
Set<HttpMethod> methods = helper.getAllowedMethods();
|
||||
if (HttpMethod.OPTIONS.equals(httpMethod)) {
|
||||
Set<MediaType> mediaTypes = helper.getConsumablePatchMediaTypes();
|
||||
HttpOptionsHandler handler = new HttpOptionsHandler(methods, mediaTypes);
|
||||
Set<MediaType> patchMediaTypes = helper.getConsumablePatchMediaTypes();
|
||||
Set<MediaType> queryMediaTypes = helper.getConsumableQueryMediaTypes();
|
||||
HttpOptionsHandler handler = new HttpOptionsHandler(methods, patchMediaTypes, queryMediaTypes);
|
||||
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
|
||||
}
|
||||
if (HttpMethod.HEAD.equals(httpMethod) && methods.contains(HttpMethod.QUERY)) {
|
||||
Set<MediaType> queryMediaTypes = helper.getConsumableQueryMediaTypes();
|
||||
HttpHeadQueryHandler handler = new HttpHeadQueryHandler(methods, queryMediaTypes);
|
||||
return new HandlerMethod(handler, HTTP_HEAD_QUERY_HANDLE_METHOD);
|
||||
}
|
||||
throw new MethodNotAllowedException(httpMethod, methods);
|
||||
}
|
||||
|
||||
@@ -323,14 +332,23 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
* PATCH specified, or that have no methods at all.
|
||||
*/
|
||||
public Set<MediaType> getConsumablePatchMediaTypes() {
|
||||
Set<MediaType> result = new LinkedHashSet<>();
|
||||
for (PartialMatch match : this.partialMatches) {
|
||||
Set<RequestMethod> methods = match.getInfo().getMethodsCondition().getMethods();
|
||||
if (methods.isEmpty() || methods.contains(RequestMethod.PATCH)) {
|
||||
result.addAll(match.getInfo().getConsumesCondition().getConsumableMediaTypes());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return getConsumableMediaTypesForMethod(RequestMethod.PATCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return declared "consumable" types but only among those that have
|
||||
* PATCH specified, or that have no methods at all.
|
||||
*/
|
||||
public Set<MediaType> getConsumableQueryMediaTypes() {
|
||||
return getConsumableMediaTypesForMethod(RequestMethod.QUERY);
|
||||
}
|
||||
|
||||
private Set<MediaType> getConsumableMediaTypesForMethod(RequestMethod method) {
|
||||
return this.partialMatches.stream()
|
||||
.map(PartialMatch::getInfo)
|
||||
.filter(info -> info.getMethodsCondition().getMethods().isEmpty() || info.getMethodsCondition().getMethods().contains(method))
|
||||
.flatMap(info -> info.getConsumesCondition().getConsumableMediaTypes().stream())
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
|
||||
@@ -400,9 +418,10 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
|
||||
public HttpOptionsHandler(Set<HttpMethod> declaredMethods, Set<MediaType> acceptPatch) {
|
||||
public HttpOptionsHandler(Set<HttpMethod> declaredMethods, Set<MediaType> acceptPatch, Set<MediaType> acceptQuery) {
|
||||
this.headers.setAllow(initAllowedHttpMethods(declaredMethods));
|
||||
this.headers.setAcceptPatch(new ArrayList<>(acceptPatch));
|
||||
this.headers.setAcceptQuery(new ArrayList<>(acceptQuery));
|
||||
}
|
||||
|
||||
private static Set<HttpMethod> initAllowedHttpMethods(Set<HttpMethod> declaredMethods) {
|
||||
@@ -413,7 +432,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
}
|
||||
else {
|
||||
Set<HttpMethod> result = new LinkedHashSet<>(declaredMethods);
|
||||
if (result.contains(HttpMethod.GET)) {
|
||||
if (result.contains(HttpMethod.GET) || result.contains(HttpMethod.QUERY)) {
|
||||
result.add(HttpMethod.HEAD);
|
||||
}
|
||||
result.add(HttpMethod.OPTIONS);
|
||||
@@ -427,4 +446,22 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default handler for HTTP HEAD targeting a QUERY endpoint.
|
||||
*/
|
||||
private static class HttpHeadQueryHandler {
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
public HttpHeadQueryHandler(Set<HttpMethod> declaredMethods, Set<MediaType> acceptQuery) {
|
||||
this.headers.setAllow(HttpOptionsHandler.initAllowedHttpMethods(declaredMethods));
|
||||
this.headers.setAcceptQuery(new ArrayList<>(acceptQuery));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public HttpHeaders handle() {
|
||||
return this.headers;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
public abstract class AbstractMessageReaderArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
private static final Set<HttpMethod> SUPPORTED_METHODS =
|
||||
Set.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
|
||||
Set.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.QUERY);
|
||||
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
+40
-3
@@ -195,6 +195,10 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
testHttpOptions("/something", Set.of(HttpMethod.PUT, HttpMethod.POST), null);
|
||||
testHttpOptions("/qux", Set.of(HttpMethod.PATCH,HttpMethod.GET,HttpMethod.HEAD,HttpMethod.OPTIONS),
|
||||
new MediaType("foo", "bar"));
|
||||
testHttpOptions("/quid", Set.of(HttpMethod.QUERY, HttpMethod.HEAD, HttpMethod.OPTIONS),
|
||||
new MediaType("application", "json"));
|
||||
testHttpHeadQuery("/quid", Set.of(HttpMethod.QUERY, HttpMethod.HEAD, HttpMethod.OPTIONS),
|
||||
new MediaType("application", "json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -377,7 +381,7 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
.isEqualTo(Collections.singletonList(new MediaType("application", "xml"))));
|
||||
}
|
||||
|
||||
private void testHttpOptions(String requestURI, Set<HttpMethod> allowedMethods, @Nullable MediaType acceptPatch) {
|
||||
private void testHttpOptions(String requestURI, Set<HttpMethod> allowedMethods, @Nullable MediaType acceptMediaType) {
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.options(requestURI));
|
||||
HandlerMethod handlerMethod = (HandlerMethod) this.handlerMapping.getHandler(exchange).block();
|
||||
|
||||
@@ -395,8 +399,36 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
HttpHeaders headers = (HttpHeaders) value;
|
||||
assertThat(headers.getAllow()).hasSameElementsAs(allowedMethods);
|
||||
|
||||
if (acceptPatch != null && headers.getAllow().contains(HttpMethod.PATCH) ) {
|
||||
assertThat(headers.getAcceptPatch()).containsExactly(acceptPatch);
|
||||
if (acceptMediaType != null) {
|
||||
if (headers.getAllow().contains(HttpMethod.PATCH)) {
|
||||
assertThat(headers.getAcceptPatch()).containsExactly(acceptMediaType);
|
||||
}
|
||||
if (headers.getAllow().contains(HttpMethod.QUERY)) {
|
||||
assertThat(headers.getAcceptQuery()).containsExactly(acceptMediaType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testHttpHeadQuery(String requestURI, Set<HttpMethod> allowedMethods, @Nullable MediaType acceptMediaType) {
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.head(requestURI));
|
||||
HandlerMethod handlerMethod = (HandlerMethod) this.handlerMapping.getHandler(exchange).block();
|
||||
|
||||
BindingContext bindingContext = new BindingContext();
|
||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
|
||||
Mono<HandlerResult> mono = invocable.invoke(exchange, bindingContext);
|
||||
|
||||
HandlerResult result = mono.block();
|
||||
assertThat(result).isNotNull();
|
||||
|
||||
Object value = result.getReturnValue();
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(value.getClass()).isEqualTo(HttpHeaders.class);
|
||||
|
||||
HttpHeaders headers = (HttpHeaders) value;
|
||||
assertThat(headers.getAllow()).hasSameElementsAs(allowedMethods);
|
||||
|
||||
if (acceptMediaType != null) {
|
||||
assertThat(headers.getAcceptQuery()).containsExactly(acceptMediaType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,6 +524,11 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
public void patchBaz(String value) {
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/quid", method = RequestMethod.QUERY, consumes = "application/json", produces = "application/json")
|
||||
public String query(@RequestBody String body) {
|
||||
return "{}";
|
||||
}
|
||||
|
||||
|
||||
public void dummy() { }
|
||||
}
|
||||
|
||||
+47
-4
@@ -69,9 +69,12 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
|
||||
private static final Method HTTP_OPTIONS_HANDLE_METHOD;
|
||||
|
||||
private static final Method HTTP_HEAD_QUERY_HANDLE_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
HTTP_OPTIONS_HANDLE_METHOD = HttpOptionsHandler.class.getMethod("handle");
|
||||
HTTP_HEAD_QUERY_HANDLE_METHOD = HttpHeadQueryHandler.class.getMethod("handle");
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Should never happen
|
||||
@@ -253,10 +256,16 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
if (helper.hasMethodsMismatch()) {
|
||||
Set<String> methods = helper.getAllowedMethods();
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
|
||||
Set<MediaType> mediaTypes = helper.getConsumablePatchMediaTypes();
|
||||
HttpOptionsHandler handler = new HttpOptionsHandler(methods, mediaTypes);
|
||||
Set<MediaType> patchMediaTypes = helper.getConsumablePatchMediaTypes();
|
||||
Set<MediaType> queryMediaTypes = helper.getConsumableQueryMediaTypes();
|
||||
HttpOptionsHandler handler = new HttpOptionsHandler(methods, patchMediaTypes, queryMediaTypes);
|
||||
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
|
||||
}
|
||||
if (HttpMethod.HEAD.matches(request.getMethod()) && methods.contains(HttpMethod.QUERY.name())) {
|
||||
Set<MediaType> queryMediaTypes = helper.getConsumableQueryMediaTypes();
|
||||
HttpHeadQueryHandler handler = new HttpHeadQueryHandler(methods, queryMediaTypes);
|
||||
return new HandlerMethod(handler, HTTP_HEAD_QUERY_HANDLE_METHOD);
|
||||
}
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
|
||||
}
|
||||
|
||||
@@ -437,6 +446,21 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return declared "consumable" types but only among those that have
|
||||
* QUERY specified, or that have no methods at all.
|
||||
*/
|
||||
public Set<MediaType> getConsumableQueryMediaTypes() {
|
||||
Set<MediaType> result = new LinkedHashSet<>();
|
||||
for (PartialMatch match : this.partialMatches) {
|
||||
Set<RequestMethod> methods = match.getInfo().getMethodsCondition().getMethods();
|
||||
if (methods.isEmpty() || methods.contains(RequestMethod.QUERY)) {
|
||||
result.addAll(match.getInfo().getConsumesCondition().getConsumableMediaTypes());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Container for a RequestMappingInfo that matches the URL path at least.
|
||||
@@ -501,9 +525,10 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
public HttpOptionsHandler(Set<String> declaredMethods, Set<MediaType> acceptPatch) {
|
||||
public HttpOptionsHandler(Set<String> declaredMethods, Set<MediaType> acceptPatch, Set<MediaType> acceptQuery) {
|
||||
this.headers.setAllow(initAllowedHttpMethods(declaredMethods));
|
||||
this.headers.setAcceptPatch(new ArrayList<>(acceptPatch));
|
||||
this.headers.setAcceptQuery(new ArrayList<>(acceptQuery));
|
||||
}
|
||||
|
||||
private static Set<HttpMethod> initAllowedHttpMethods(Set<String> declaredMethods) {
|
||||
@@ -519,7 +544,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
for (String method : declaredMethods) {
|
||||
HttpMethod httpMethod = HttpMethod.valueOf(method);
|
||||
result.add(httpMethod);
|
||||
if (httpMethod == HttpMethod.GET) {
|
||||
if (httpMethod == HttpMethod.GET || httpMethod == HttpMethod.QUERY) {
|
||||
result.add(HttpMethod.HEAD);
|
||||
}
|
||||
}
|
||||
@@ -534,4 +559,22 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default handler for HTTP HEAD targeting a QUERY endpoint.
|
||||
*/
|
||||
private static class HttpHeadQueryHandler {
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
public HttpHeadQueryHandler(Set<String> declaredMethods, Set<MediaType> acceptQuery) {
|
||||
this.headers.setAllow(HttpOptionsHandler.initAllowedHttpMethods(declaredMethods));
|
||||
this.headers.setAcceptQuery(new ArrayList<>(acceptQuery));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public HttpHeaders handle() {
|
||||
return this.headers;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
protected enum ConverterType { BASE, GENERIC, SMART };
|
||||
|
||||
|
||||
private static final Set<HttpMethod> SUPPORTED_METHODS = Set.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
|
||||
private static final Set<HttpMethod> SUPPORTED_METHODS = Set.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.QUERY);
|
||||
|
||||
private static final Object NO_VALUE = new Object();
|
||||
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ class MappedInterceptorTests {
|
||||
testHttpMethods(
|
||||
new HttpMethod[] {},
|
||||
new HttpMethod[] {HttpMethod.GET, HttpMethod.POST, HttpMethod.OPTIONS},
|
||||
"HEAD", "PUT", "DELETE", "TRACE", "PATCH");
|
||||
"HEAD", "PUT", "DELETE", "TRACE", "PATCH", "QUERY");
|
||||
}
|
||||
|
||||
private void testHttpMethods(HttpMethod[] include, HttpMethod[] exclude, String... expected) {
|
||||
|
||||
+31
-1
@@ -193,9 +193,11 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
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,OPTIONS,QUERY", null);
|
||||
testHttpOptions(mapping, "/something", "PUT,POST", null);
|
||||
testHttpOptions(mapping, "/qux", "PATCH,GET,HEAD,OPTIONS", new MediaType("foo", "bar"));
|
||||
testHttpOptions(mapping, "/quid", "QUERY,HEAD,OPTIONS", null);
|
||||
testHttpHeadQuery(mapping, "/quid", "QUERY,HEAD,OPTIONS", MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
@PathPatternsParameterizedTest
|
||||
@@ -477,6 +479,29 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
}
|
||||
}
|
||||
|
||||
private void testHttpHeadQuery(TestRequestMappingInfoHandlerMapping mapping, String requestURI,
|
||||
String allowHeader, @Nullable MediaType acceptQuery) throws Exception {
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("HEAD", requestURI);
|
||||
HandlerMethod handlerMethod = getHandler(mapping, request);
|
||||
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request);
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
Object result = new InvocableHandlerMethod(handlerMethod).invokeForRequest(webRequest, mavContainer);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getClass()).isEqualTo(HttpHeaders.class);
|
||||
HttpHeaders headers = (HttpHeaders) result;
|
||||
Set<HttpMethod> allowedMethods = Arrays.stream(allowHeader.split(","))
|
||||
.map(HttpMethod::valueOf)
|
||||
.collect(Collectors.toSet());
|
||||
assertThat(headers.getAllow()).hasSameElementsAs(allowedMethods);
|
||||
|
||||
if (acceptQuery != null) {
|
||||
assertThat(headers.getAcceptQuery()).containsExactly(acceptQuery);
|
||||
}
|
||||
}
|
||||
|
||||
private void testHttpMediaTypeNotAcceptableException(TestRequestMappingInfoHandlerMapping mapping, String url) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", url);
|
||||
request.addHeader("Accept", "application/json");
|
||||
@@ -572,6 +597,11 @@ class RequestMappingInfoHandlerMappingTests {
|
||||
@RequestMapping(value = "/qux", method = RequestMethod.PATCH, consumes = "foo/bar")
|
||||
public void patchBaz(String value) {
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/quid", method = RequestMethod.QUERY, consumes = "application/json", produces = "application/json")
|
||||
public String query(@RequestBody String body) {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+16
@@ -148,6 +148,22 @@ class ResponseEntityExceptionHandlerTests {
|
||||
assertThat(headers.getFirst(HttpHeaders.ACCEPT_PATCH)).isEqualTo("application/atom+xml, application/xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryHttpMediaTypeNotSupported() {
|
||||
this.servletRequest = new MockHttpServletRequest("QUERY", "/");
|
||||
this.request = new ServletWebRequest(this.servletRequest, this.servletResponse);
|
||||
|
||||
ResponseEntity<Object> entity = testException(
|
||||
new HttpMediaTypeNotSupportedException(
|
||||
MediaType.APPLICATION_JSON,
|
||||
List.of(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML),
|
||||
HttpMethod.QUERY));
|
||||
|
||||
HttpHeaders headers = entity.getHeaders();
|
||||
assertThat(headers.getFirst(HttpHeaders.ACCEPT)).isEqualTo("application/atom+xml, application/xml");
|
||||
assertThat(headers.getFirst(HttpHeaders.ACCEPT_QUERY)).isEqualTo("application/atom+xml, application/xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpMediaTypeNotAcceptable() {
|
||||
testException(new HttpMediaTypeNotAcceptableException(""));
|
||||
|
||||
+2
-2
@@ -39,7 +39,7 @@ class WebContentGeneratorTests {
|
||||
@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,OPTIONS,QUERY");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +59,7 @@ class WebContentGeneratorTests {
|
||||
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,OPTIONS,QUERY");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user