Skip to content

Commit 6e335e3

Browse files
committed
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
1 parent f57004d commit 6e335e3

File tree

2 files changed

+207
-4
lines changed

2 files changed

+207
-4
lines changed

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

+131-4
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,147 @@
1616

1717
package org.springframework.http;
1818

19+
import java.io.Serializable;
1920
import java.util.HashMap;
2021
import java.util.Map;
2122

2223
import org.springframework.lang.Nullable;
24+
import org.springframework.util.Assert;
2325

2426
/**
25-
* Java 5 enumeration of HTTP request methods. Intended for use
27+
* Represents an HTTP request methods. Intended for use
2628
* with {@link org.springframework.http.client.ClientHttpRequest}
2729
* and {@link org.springframework.web.client.RestTemplate}.
2830
*
2931
* @author Arjen Poutsma
3032
* @author Juergen Hoeller
3133
* @since 3.0
3234
*/
33-
public enum HttpMethod {
35+
public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
3436

35-
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
37+
private static final long serialVersionUID = -70133475680645360L;
3638

39+
private static final HttpMethod[] values;
3740

3841
private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
3942

43+
44+
/**
45+
* The HTTP method {@code GET}.
46+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3">HTTP 1.1, section 9.3</a>
47+
*/
48+
public static final HttpMethod GET = new HttpMethod("GET");
49+
50+
/**
51+
* The HTTP method {@code HEAD}.
52+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4">HTTP 1.1, section 9.4</a>
53+
*/
54+
public static final HttpMethod HEAD = new HttpMethod("HEAD");
55+
56+
/**
57+
* The HTTP method {@code POST}.
58+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5">HTTP 1.1, section 9.5</a>
59+
*/
60+
public static final HttpMethod POST = new HttpMethod("POST");
61+
62+
/**
63+
* The HTTP method {@code PUT}.
64+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6">HTTP 1.1, section 9.6</a>
65+
*/
66+
public static final HttpMethod PUT = new HttpMethod("PUT");
67+
68+
/**
69+
* The HTTP method {@code PATCH}.
70+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc5789#section-2">RFC 5789</a>
71+
*/
72+
public static final HttpMethod PATCH = new HttpMethod("PATCH");
73+
74+
/**
75+
* The HTTP method {@code DELETE}.
76+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7">HTTP 1.1, section 9.7</a>
77+
*/
78+
public static final HttpMethod DELETE = new HttpMethod("DELETE");
79+
80+
/**
81+
* The HTTP method {@code OPTIONS}.
82+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2">HTTP 1.1, section 9.2</a>
83+
*/
84+
public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS");
85+
86+
/**
87+
* The HTTP method {@code TRACE}.
88+
* @see <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8">HTTP 1.1, section 9.8</a>
89+
*/
90+
public static final HttpMethod TRACE = new HttpMethod("TRACE");
91+
92+
4093
static {
41-
for (HttpMethod httpMethod : values()) {
94+
values = new HttpMethod[]{GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE};
95+
for (HttpMethod httpMethod : values) {
4296
mappings.put(httpMethod.name(), httpMethod);
4397
}
4498
}
4599

46100

101+
private final String name;
102+
103+
104+
private HttpMethod(String name) {
105+
this.name = name;
106+
}
107+
108+
/**
109+
* Returns an array containing the standard HTTP methods. Specifically,
110+
* this method returns an array containing {@link #GET}, {@link #HEAD},
111+
* {@link #POST}, {@link #PUT}, {@link #PATCH}, {@link #DELETE},
112+
* {@link #OPTIONS}, and {@link #TRACE}.
113+
*
114+
* <p>Note that the returned value does not include any HTTP methods defined
115+
* in WebDav.
116+
*/
117+
public static HttpMethod[] values() {
118+
HttpMethod[] copy = new HttpMethod[values.length];
119+
System.arraycopy(values, 0, copy, 0, values.length);
120+
return copy;
121+
}
122+
123+
/**
124+
* Return an {@code HttpMethod} object for the given value.
125+
* @param method the method value as a String
126+
* @return the corresponding {@code HttpMethod}
127+
*/
128+
public static HttpMethod valueOf(String method) {
129+
Assert.notNull(method, "Name must not be null");
130+
HttpMethod result = mappings.get(method);
131+
if (result != null) {
132+
return result;
133+
}
134+
else {
135+
return new HttpMethod(method);
136+
}
137+
}
138+
47139
/**
48140
* Resolve the given method value to an {@code HttpMethod}.
49141
* @param method the method value as a String
50142
* @return the corresponding {@code HttpMethod}, or {@code null} if not found
51143
* @since 4.2.4
144+
* @deprecated in favor of {@link #valueOf(String)}
52145
*/
53146
@Nullable
147+
@Deprecated
54148
public static HttpMethod resolve(@Nullable String method) {
55149
return (method != null ? mappings.get(method) : null);
56150
}
57151

58152

153+
/**
154+
* Return the name of this method, e.g. "GET", "POST".
155+
*/
156+
public String name() {
157+
return this.name;
158+
}
159+
59160
/**
60161
* Determine whether this {@code HttpMethod} matches the given method value.
61162
* @param method the HTTP method as a String
@@ -66,4 +167,30 @@ public boolean matches(String method) {
66167
return name().equals(method);
67168
}
68169

170+
171+
@Override
172+
public int compareTo(HttpMethod other) {
173+
return this.name.compareTo(other.name);
174+
}
175+
176+
@Override
177+
public int hashCode() {
178+
return this.name.hashCode();
179+
}
180+
181+
@Override
182+
public boolean equals(Object o) {
183+
if (this == o) {
184+
return true;
185+
}
186+
else if (o instanceof HttpMethod other) {
187+
return this.name.equals(other.name);
188+
}
189+
return false;
190+
}
191+
192+
@Override
193+
public String toString() {
194+
return this.name;
195+
}
69196
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.http;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
/**
24+
* @author Arjen Poutsma
25+
*/
26+
class HttpMethodTests {
27+
28+
@Test
29+
public void comparison() {
30+
HttpMethod method1 = HttpMethod.valueOf("FOO");
31+
HttpMethod method2 = HttpMethod.valueOf("FOO");
32+
HttpMethod method3 = HttpMethod.valueOf("BAR");
33+
34+
assertThat(method1).isEqualTo(method2);
35+
assertThat(method1).isNotEqualTo(method3);
36+
37+
assertThat(method1.hashCode()).isEqualTo(method2.hashCode());
38+
39+
assertThat(method1.compareTo(method2)).isEqualTo(0);
40+
assertThat(method1.compareTo(method3)).isNotEqualTo(0);
41+
}
42+
43+
@Test
44+
void values() {
45+
HttpMethod[] values = HttpMethod.values();
46+
assertThat(values).containsExactly(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.POST, HttpMethod.PUT,
47+
HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.OPTIONS, HttpMethod.TRACE);
48+
49+
// check defensive copy
50+
values[0] = HttpMethod.POST;
51+
assertThat(HttpMethod.values()).containsExactly(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.POST, HttpMethod.PUT,
52+
HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.OPTIONS, HttpMethod.TRACE);
53+
}
54+
55+
@Test
56+
void valueOf() {
57+
HttpMethod get = HttpMethod.valueOf("GET");
58+
assertThat(get).isSameAs(HttpMethod.GET);
59+
60+
HttpMethod foo = HttpMethod.valueOf("FOO");
61+
HttpMethod other = HttpMethod.valueOf("FOO");
62+
assertThat(foo).isEqualTo(other);
63+
}
64+
65+
@Test
66+
void name() {
67+
HttpMethod method = HttpMethod.valueOf("FOO");
68+
assertThat(method.name()).isEqualTo("FOO");
69+
}
70+
71+
@Test
72+
void matches() {
73+
assertThat(HttpMethod.GET.matches("GET")).isTrue();
74+
assertThat(HttpMethod.GET.matches("FOO")).isFalse();
75+
}
76+
}

0 commit comments

Comments
 (0)