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 126ed3bb747e..f3b30c9c6d0c 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpMethod.java +++ b/spring-web/src/main/java/org/springframework/http/HttpMethod.java @@ -17,10 +17,6 @@ package org.springframework.http; import java.io.Serializable; -import java.util.Arrays; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -88,9 +84,6 @@ public final class HttpMethod implements Comparable, Serializable { private static final HttpMethod[] values = new HttpMethod[] { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }; - private static final Map mappings = Arrays.stream(values) - .collect(Collectors.toUnmodifiableMap(HttpMethod::name, Function.identity())); - private final String name; @@ -121,13 +114,17 @@ public static HttpMethod[] values() { */ public static HttpMethod valueOf(String method) { Assert.notNull(method, "Method must not be null"); - HttpMethod result = mappings.get(method); - if (result != null) { - return result; - } - else { - return new HttpMethod(method); - } + return switch (method) { + case "GET" -> GET; + case "HEAD" -> HEAD; + case "POST" -> POST; + case "PUT" -> PUT; + case "PATCH" -> PATCH; + case "DELETE" -> DELETE; + case "OPTIONS" -> OPTIONS; + case "TRACE" -> TRACE; + default -> new HttpMethod(method); + }; } /** 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 bc95c85695d4..ce14d68392a8 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 @@ -16,6 +16,10 @@ package org.springframework.web.bind.annotation; +import org.springframework.http.HttpMethod; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + /** * Enumeration of HTTP request methods. Intended for use with the * {@link RequestMapping#method()} attribute of the {@link RequestMapping} annotation. @@ -34,6 +38,62 @@ */ public enum RequestMethod { - GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + + /** + * Resolve the given method value to an {@code RequestMethod} enum value. + * Returns {@code null} if {@code method} has no corresponding value. + * @param method the method value as a String + * @return the corresponding {@code RequestMethod}, or {@code null} if not found + * @since 6.0.6 + */ + @Nullable + public static RequestMethod resolve(String method) { + Assert.notNull(method, "Method must not be null"); + return switch (method) { + case "GET" -> GET; + case "HEAD" -> HEAD; + case "POST" -> POST; + case "PUT" -> PUT; + case "PATCH" -> PATCH; + case "DELETE" -> DELETE; + case "OPTIONS" -> OPTIONS; + case "TRACE" -> TRACE; + default -> null; + }; + } + + /** + * Resolve the given {@link HttpMethod} to a {@code RequestMethod} enum value. + * Returns {@code null} if {@code httpMethod} has no corresponding value. + * @param httpMethod the http method object + * @return the corresponding {@code RequestMethod}, or {@code null} if not found + * @since 6.0.6 + */ + @Nullable + public static RequestMethod resolve(HttpMethod httpMethod) { + Assert.notNull(httpMethod, "HttpMethod must not be null"); + return resolve(httpMethod.name()); + } + + + /** + * Return the {@link HttpMethod} corresponding to this {@code RequestMethod}. + * @return the http method for this request method + * @since 6.0.6 + */ + public HttpMethod asHttpMethod() { + return switch (this) { + case GET -> HttpMethod.GET; + case HEAD -> HttpMethod.HEAD; + case POST -> HttpMethod.POST; + case PUT -> HttpMethod.PUT; + case PATCH -> HttpMethod.PATCH; + case DELETE -> HttpMethod.DELETE; + case OPTIONS -> HttpMethod.OPTIONS; + case TRACE -> HttpMethod.TRACE; + }; + } } 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 new file mode 100644 index 000000000000..7c628c91922e --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/bind/annotation/RequestMethodTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.bind.annotation; + +import org.junit.jupiter.api.Test; + +import org.springframework.http.HttpMethod; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Arjen Poutsma + */ +class RequestMethodTests { + + @Test + void resolveString() { + String[] methods = new String[]{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "TRACE"}; + for (String httpMethod : methods) { + RequestMethod requestMethod = RequestMethod.resolve(httpMethod); + assertThat(requestMethod).isNotNull(); + assertThat(requestMethod.name()).isEqualTo(httpMethod); + } + assertThat(RequestMethod.resolve("PROPFIND")).isNull(); + } + + @Test + void resolveHttpMethod() { + for (HttpMethod httpMethod : HttpMethod.values()) { + RequestMethod requestMethod = RequestMethod.resolve(httpMethod); + assertThat(requestMethod).isNotNull(); + assertThat(requestMethod.name()).isEqualTo(httpMethod.name()); + } + assertThat(RequestMethod.resolve(HttpMethod.valueOf("PROPFIND"))).isNull(); + } + + @Test + void asHttpMethod() { + for (RequestMethod requestMethod : RequestMethod.values()) { + HttpMethod httpMethod = requestMethod.asHttpMethod(); + assertThat(httpMethod).isNotNull(); + assertThat(httpMethod.name()).isEqualTo(requestMethod.name()); + } + } +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java index 600708a77365..445d110052fa 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java @@ -46,9 +46,9 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi static { requestMethodConditionCache = CollectionUtils.newHashMap(RequestMethod.values().length); - for (RequestMethod method : RequestMethod.values()) { - requestMethodConditionCache.put( - HttpMethod.valueOf(method.name()), new RequestMethodsRequestCondition(method)); + for (RequestMethod requestMethod : RequestMethod.values()) { + requestMethodConditionCache.put(requestMethod.asHttpMethod(), + new RequestMethodsRequestCondition(requestMethod)); } } @@ -150,16 +150,15 @@ private RequestMethodsRequestCondition matchPreFlight(ServerHttpRequest request) } @Nullable - private RequestMethodsRequestCondition matchRequestMethod(@Nullable HttpMethod httpMethod) { - if (httpMethod == null) { - return null; - } - RequestMethod requestMethod = RequestMethod.valueOf(httpMethod.name()); - if (getMethods().contains(requestMethod)) { - return requestMethodConditionCache.get(httpMethod); - } - if (requestMethod.equals(RequestMethod.HEAD) && getMethods().contains(RequestMethod.GET)) { - return requestMethodConditionCache.get(HttpMethod.GET); + private RequestMethodsRequestCondition matchRequestMethod(HttpMethod httpMethod) { + RequestMethod requestMethod = RequestMethod.resolve(httpMethod); + if (requestMethod != null) { + if (getMethods().contains(requestMethod)) { + return requestMethodConditionCache.get(httpMethod); + } + if (requestMethod.equals(RequestMethod.HEAD) && getMethods().contains(RequestMethod.GET)) { + return requestMethodConditionCache.get(HttpMethod.GET); + } } return null; } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestConditionTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestConditionTests.java index 5d90c01c66ca..a4f00403d795 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestConditionTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestConditionTests.java @@ -19,7 +19,6 @@ import java.net.URISyntaxException; import java.util.Collections; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; @@ -44,8 +43,6 @@ */ public class RequestMethodsRequestConditionTests { - // TODO: custom method, CORS pre-flight (see @Disabled) - @Test public void getMatchingCondition() throws Exception { testMatch(new RequestMethodsRequestCondition(GET), GET); @@ -73,7 +70,6 @@ public void getMatchingConditionWithEmptyConditions() throws Exception { } @Test - @Disabled public void getMatchingConditionWithCustomMethod() throws Exception { ServerWebExchange exchange = getExchange("PROPFIND"); assertThat(new RequestMethodsRequestCondition().getMatchingCondition(exchange)).isNotNull(); @@ -81,11 +77,12 @@ public void getMatchingConditionWithCustomMethod() throws Exception { } @Test - @Disabled - public void getMatchingConditionWithCorsPreFlight() throws Exception { - ServerWebExchange exchange = getExchange("OPTIONS"); - exchange.getRequest().getHeaders().add("Origin", "https://example.com"); - exchange.getRequest().getHeaders().add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + public void getMatchingConditionWithCorsPreFlight() { + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.valueOf("OPTIONS"), "/") + .header("Origin", "https://example.com") + .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT") + .build(); + ServerWebExchange exchange = MockServerWebExchange.from(request); assertThat(new RequestMethodsRequestCondition().getMatchingCondition(exchange)).isNotNull(); assertThat(new RequestMethodsRequestCondition(PUT).getMatchingCondition(exchange)).isNotNull(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java index f00f2414a73f..db163aedf940 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java @@ -157,9 +157,8 @@ private RequestMethodsRequestCondition matchPreFlight(HttpServletRequest request @Nullable private RequestMethodsRequestCondition matchRequestMethod(String httpMethodValue) { - RequestMethod requestMethod; - try { - requestMethod = RequestMethod.valueOf(httpMethodValue); + RequestMethod requestMethod = RequestMethod.resolve(httpMethodValue); + if (requestMethod != null) { if (getMethods().contains(requestMethod)) { return requestMethodConditionCache.get(httpMethodValue); } @@ -167,9 +166,6 @@ private RequestMethodsRequestCondition matchRequestMethod(String httpMethodValue return requestMethodConditionCache.get(HttpMethod.GET.name()); } } - catch (IllegalArgumentException ex) { - // Custom request method - } return null; }