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 b1039145cf..0cb5885a0f 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpMethod.java +++ b/spring-web/src/main/java/org/springframework/http/HttpMethod.java @@ -16,13 +16,15 @@ package org.springframework.http; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** - * Java 5 enumeration of HTTP request methods. Intended for use + * Represents an HTTP request methods. Intended for use * with {@link org.springframework.http.client.ClientHttpRequest} * and {@link org.springframework.web.client.RestTemplate}. * @@ -30,32 +32,131 @@ import org.springframework.lang.Nullable; * @author Juergen Hoeller * @since 3.0 */ -public enum HttpMethod { +public final class HttpMethod implements Comparable, Serializable { - GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + private static final long serialVersionUID = -70133475680645360L; + private static final HttpMethod[] values; private static final Map mappings = new HashMap<>(16); + + /** + * The HTTP method {@code GET}. + * @see HTTP 1.1, section 9.3 + */ + public static final HttpMethod GET = new HttpMethod("GET"); + + /** + * The HTTP method {@code HEAD}. + * @see HTTP 1.1, section 9.4 + */ + public static final HttpMethod HEAD = new HttpMethod("HEAD"); + + /** + * The HTTP method {@code POST}. + * @see HTTP 1.1, section 9.5 + */ + public static final HttpMethod POST = new HttpMethod("POST"); + + /** + * The HTTP method {@code PUT}. + * @see HTTP 1.1, section 9.6 + */ + public static final HttpMethod PUT = new HttpMethod("PUT"); + + /** + * The HTTP method {@code PATCH}. + * @see RFC 5789 + */ + public static final HttpMethod PATCH = new HttpMethod("PATCH"); + + /** + * The HTTP method {@code DELETE}. + * @see HTTP 1.1, section 9.7 + */ + public static final HttpMethod DELETE = new HttpMethod("DELETE"); + + /** + * The HTTP method {@code OPTIONS}. + * @see HTTP 1.1, section 9.2 + */ + public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS"); + + /** + * The HTTP method {@code TRACE}. + * @see HTTP 1.1, section 9.8 + */ + public static final HttpMethod TRACE = new HttpMethod("TRACE"); + + static { - for (HttpMethod httpMethod : values()) { + values = new HttpMethod[]{GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE}; + for (HttpMethod httpMethod : values) { mappings.put(httpMethod.name(), httpMethod); } } + private final String name; + + + private HttpMethod(String name) { + this.name = 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 #OPTIONS}, and {@link #TRACE}. + * + *

Note that the returned value does not include any HTTP methods defined + * in WebDav. + */ + public static HttpMethod[] values() { + HttpMethod[] copy = new HttpMethod[values.length]; + System.arraycopy(values, 0, copy, 0, values.length); + return copy; + } + + /** + * Return an {@code HttpMethod} object for the given value. + * @param method the method value as a String + * @return the corresponding {@code HttpMethod} + */ + public static HttpMethod valueOf(String method) { + Assert.notNull(method, "Name must not be null"); + HttpMethod result = mappings.get(method); + if (result != null) { + return result; + } + else { + return new HttpMethod(method); + } + } + /** * Resolve the given method value to an {@code HttpMethod}. * @param method the method value as a String * @return the corresponding {@code HttpMethod}, or {@code null} if not found * @since 4.2.4 + * @deprecated in favor of {@link #valueOf(String)} */ @Nullable + @Deprecated public static HttpMethod resolve(@Nullable String method) { return (method != null ? mappings.get(method) : null); } + /** + * Return the name of this method, e.g. "GET", "POST". + */ + public String name() { + return this.name; + } + /** * Determine whether this {@code HttpMethod} matches the given method value. * @param method the HTTP method as a String @@ -66,4 +167,30 @@ public enum HttpMethod { return name().equals(method); } + + @Override + public int compareTo(HttpMethod other) { + return this.name.compareTo(other.name); + } + + @Override + public int hashCode() { + return this.name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + else if (o instanceof HttpMethod other) { + return this.name.equals(other.name); + } + return false; + } + + @Override + public String toString() { + return this.name; + } } diff --git a/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java b/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java new file mode 100644 index 0000000000..ee92695ac9 --- /dev/null +++ b/spring-web/src/test/java/org/springframework/http/HttpMethodTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2021 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.http; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Arjen Poutsma + */ +class HttpMethodTests { + + @Test + public void comparison() { + HttpMethod method1 = HttpMethod.valueOf("FOO"); + HttpMethod method2 = HttpMethod.valueOf("FOO"); + HttpMethod method3 = HttpMethod.valueOf("BAR"); + + assertThat(method1).isEqualTo(method2); + assertThat(method1).isNotEqualTo(method3); + + assertThat(method1.hashCode()).isEqualTo(method2.hashCode()); + + assertThat(method1.compareTo(method2)).isEqualTo(0); + assertThat(method1.compareTo(method3)).isNotEqualTo(0); + } + + @Test + 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); + + // 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); + } + + @Test + void valueOf() { + HttpMethod get = HttpMethod.valueOf("GET"); + assertThat(get).isSameAs(HttpMethod.GET); + + HttpMethod foo = HttpMethod.valueOf("FOO"); + HttpMethod other = HttpMethod.valueOf("FOO"); + assertThat(foo).isEqualTo(other); + } + + @Test + void name() { + HttpMethod method = HttpMethod.valueOf("FOO"); + assertThat(method.name()).isEqualTo("FOO"); + } + + @Test + void matches() { + assertThat(HttpMethod.GET.matches("GET")).isTrue(); + assertThat(HttpMethod.GET.matches("FOO")).isFalse(); + } +}