Refactor HTTP Method from Enum to Class
This commit refactors HttpMethod from a Java enum into a class. The underlying reason being that HTTP methods are not enumerable, but instead an open range and not limited to the predefined values in the specifications. Closes gh-27697
This commit is contained in:
parent
f57004db2c
commit
6e335e3a9f
|
|
@ -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<HttpMethod>, Serializable {
|
||||
|
||||
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
|
||||
private static final long serialVersionUID = -70133475680645360L;
|
||||
|
||||
private static final HttpMethod[] values;
|
||||
|
||||
private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
|
||||
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
public static final HttpMethod PUT = new HttpMethod("PUT");
|
||||
|
||||
/**
|
||||
* The HTTP method {@code PATCH}.
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc5789#section-2">RFC 5789</a>
|
||||
*/
|
||||
public static final HttpMethod PATCH = new HttpMethod("PATCH");
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue