Skip to content

Commit 058279b

Browse files
committed
HEAD mapping has higher priority over other conditions
When comparing multiple matching @RequestMapping's, the HTTP method condition has the lowest precedence. It's mainly about ensuring an explicit mapping wins over an implicit (i.e. no method) one. As of 4.3 HTTP HEAD is handled automatically for controller methods that match to GET. However an explicit mapping HTTP HEAD allows an application to take control. This commit ensures that for HTTP HEAD requests the HTTP method condition is checked first which means that an explicit HEAD mapping now trumps all other conditions. Normally we look for the most specific matching @RequestMapping. For HTTP HEAD we now look for the most specific match among @RequestMapping methods with a HEAD mapping first. Issue: SPR-14383
1 parent db1092f commit 058279b

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020
import javax.servlet.http.HttpServletRequest;
2121

22+
import org.springframework.http.HttpMethod;
2223
import org.springframework.util.PathMatcher;
2324
import org.springframework.util.StringUtils;
2425
import org.springframework.web.accept.ContentNegotiationManager;
@@ -238,7 +239,15 @@ public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
238239
*/
239240
@Override
240241
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
241-
int result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
242+
int result;
243+
// Automatic vs explicit HTTP HEAD mapping
244+
if (HttpMethod.HEAD.matches(request.getMethod())) {
245+
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
246+
if (result != 0) {
247+
return result;
248+
}
249+
}
250+
result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
242251
if (result != 0) {
243252
return result;
244253
}
@@ -258,6 +267,7 @@ public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
258267
if (result != 0) {
259268
return result;
260269
}
270+
// Implicit (no method) vs explicit HTTP method mappings
261271
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
262272
if (result != 0) {
263273
return result;

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static org.junit.Assert.assertNotNull;
3535
import static org.junit.Assert.assertNull;
3636
import static org.springframework.web.bind.annotation.RequestMethod.GET;
37+
import static org.springframework.web.bind.annotation.RequestMethod.HEAD;
3738
import static org.springframework.web.servlet.mvc.method.RequestMappingInfo.paths;
3839

3940
/**
@@ -171,6 +172,27 @@ public void compareToWithImpicitVsExplicitHttpMethodDeclaration() {
171172
assertEquals(noMethods, list.get(2));
172173
}
173174

175+
@Test // SPR-14383
176+
public void compareToWithHttpHeadMapping() {
177+
MockHttpServletRequest request = new MockHttpServletRequest();
178+
request.setMethod("HEAD");
179+
request.addHeader("Accept", "application/json");
180+
181+
RequestMappingInfo noMethods = paths().build();
182+
RequestMappingInfo getMethod = paths().methods(GET).produces("application/json").build();
183+
RequestMappingInfo headMethod = paths().methods(HEAD).build();
184+
185+
Comparator<RequestMappingInfo> comparator = (info, otherInfo) -> info.compareTo(otherInfo, request);
186+
187+
List<RequestMappingInfo> list = asList(noMethods, getMethod, headMethod);
188+
Collections.shuffle(list);
189+
Collections.sort(list, comparator);
190+
191+
assertEquals(headMethod, list.get(0));
192+
assertEquals(getMethod, list.get(1));
193+
assertEquals(noMethods, list.get(2));
194+
}
195+
174196
@Test
175197
public void equals() {
176198
RequestMappingInfo info1 = paths("/foo").methods(GET)

0 commit comments

Comments
 (0)