diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java index e529f6101f..dfdfc05ed2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java @@ -152,30 +152,47 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe */ @Override protected HandlerMethod handleNoMatch(Set requestMappingInfos, - String lookupPath, - HttpServletRequest request) throws ServletException { + String lookupPath, HttpServletRequest request) throws ServletException { + Set allowedMethods = new HashSet(6); - Set consumableMediaTypes = new HashSet(); - Set producibleMediaTypes = new HashSet(); + + Set patternMatches = new HashSet(); + Set patternAndMethodMatches = new HashSet(); + for (RequestMappingInfo info : requestMappingInfos) { if (info.getPatternsCondition().getMatchingCondition(request) != null) { - if (info.getMethodsCondition().getMatchingCondition(request) == null) { + patternMatches.add(info); + if (info.getMethodsCondition().getMatchingCondition(request) != null) { + patternAndMethodMatches.add(info); + } + else { for (RequestMethod method : info.getMethodsCondition().getMethods()) { allowedMethods.add(method.name()); } } - if (info.getConsumesCondition().getMatchingCondition(request) == null) { - consumableMediaTypes.addAll(info.getConsumesCondition().getConsumableMediaTypes()); - } - if (info.getProducesCondition().getMatchingCondition(request) == null) { - producibleMediaTypes.addAll(info.getProducesCondition().getProducibleMediaTypes()); - } } } - if (!allowedMethods.isEmpty()) { + + if (patternMatches.isEmpty()) { + return null; + } + else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) { throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods); } - else if (!consumableMediaTypes.isEmpty()) { + + Set consumableMediaTypes; + Set producibleMediaTypes; + + if (patternAndMethodMatches.isEmpty()) { + consumableMediaTypes = getConsumableMediaTypes(request, patternMatches); + producibleMediaTypes = getProdicubleMediaTypes(request, patternMatches); + } + else { + consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches); + producibleMediaTypes = getProdicubleMediaTypes(request, patternAndMethodMatches); + } + + if (!consumableMediaTypes.isEmpty()) { MediaType contentType = null; if (StringUtils.hasLength(request.getContentType())) { contentType = MediaType.parseMediaType(request.getContentType()); @@ -190,4 +207,24 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe } } + private Set getConsumableMediaTypes(HttpServletRequest request, Set partialMatches) { + Set result = new HashSet(); + for (RequestMappingInfo partialMatch : partialMatches) { + if (partialMatch.getConsumesCondition().getMatchingCondition(request) == null) { + result.addAll(partialMatch.getConsumesCondition().getConsumableMediaTypes()); + } + } + return result; + } + + private Set getProdicubleMediaTypes(HttpServletRequest request, Set partialMatches) { + Set result = new HashSet(); + for (RequestMappingInfo partialMatch : partialMatches) { + if (partialMatch.getProducesCondition().getMatchingCondition(request) == null) { + result.addAll(partialMatch.getProducesCondition().getProducibleMediaTypes()); + } + } + return result; + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index deea55a349..6c34a0fbff 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -70,8 +70,6 @@ public class RequestMappingInfoHandlerMappingTests { private TestRequestMappingInfoHandlerMapping handlerMapping; - private Handler handler; - private HandlerMethod fooMethod; private HandlerMethod fooParamMethod; @@ -82,14 +80,15 @@ public class RequestMappingInfoHandlerMappingTests { @Before public void setUp() throws Exception { - this.handler = new Handler(); - this.fooMethod = new HandlerMethod(handler, "foo"); - this.fooParamMethod = new HandlerMethod(handler, "fooParam"); - this.barMethod = new HandlerMethod(handler, "bar"); - this.emptyMethod = new HandlerMethod(handler, "empty"); + TestController testController = new TestController(); + + this.fooMethod = new HandlerMethod(testController, "foo"); + this.fooParamMethod = new HandlerMethod(testController, "fooParam"); + this.barMethod = new HandlerMethod(testController, "bar"); + this.emptyMethod = new HandlerMethod(testController, "empty"); this.handlerMapping = new TestRequestMappingInfoHandlerMapping(); - this.handlerMapping.registerHandler(this.handler); + this.handlerMapping.registerHandler(testController); this.handlerMapping.setRemoveSemicolonContent(false); } @@ -148,10 +147,23 @@ public class RequestMappingInfoHandlerMappingTests { } } + // SPR-9603 + + @Test(expected=HttpMediaTypeNotAcceptableException.class) + public void requestMethodMatchFalsePositive() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/users"); + request.addHeader("Accept", "application/xml"); + + this.handlerMapping.registerHandler(new UserController()); + this.handlerMapping.getHandler(request); + } + @Test public void mediaTypeNotSupported() throws Exception { testMediaTypeNotSupported("/person/1"); - testMediaTypeNotSupported("/person/1/"); // SPR-8462 + + // SPR-8462 + testMediaTypeNotSupported("/person/1/"); testMediaTypeNotSupported("/person/1.json"); } @@ -171,7 +183,9 @@ public class RequestMappingInfoHandlerMappingTests { @Test public void mediaTypeNotAccepted() throws Exception { testMediaTypeNotAccepted("/persons"); - testMediaTypeNotAccepted("/persons/"); // SPR-8462 + + // SPR-8462 + testMediaTypeNotAccepted("/persons/"); testMediaTypeNotAccepted("/persons.json"); } @@ -276,7 +290,7 @@ public class RequestMappingInfoHandlerMappingTests { MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] {path}, interceptor); TestRequestMappingInfoHandlerMapping hm = new TestRequestMappingInfoHandlerMapping(); - hm.registerHandler(this.handler); + hm.registerHandler(new TestController()); hm.setInterceptors(new Object[] { mappedInterceptor }); hm.setApplicationContext(new StaticWebApplicationContext()); @@ -358,7 +372,7 @@ public class RequestMappingInfoHandlerMappingTests { @Controller - private static class Handler { + private static class TestController { @RequestMapping(value = "/foo", method = RequestMethod.GET) public void foo() { @@ -396,6 +410,18 @@ public class RequestMappingInfoHandlerMappingTests { } } + @Controller + private static class UserController { + + @RequestMapping(value = "/users", method = RequestMethod.GET, produces = "application/json") + public void getUser() { + } + + @RequestMapping(value = "/users", method = RequestMethod.PUT) + public void saveUser() { + } + } + private static class TestRequestMappingInfoHandlerMapping extends RequestMappingInfoHandlerMapping { public void registerHandler(Object handler) {