diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java index 6ef6afc1e7c..05add0002ae 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodMapping.java @@ -129,7 +129,8 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap private static RequestMappingInfo createFromRequestMapping(RequestMapping annotation) { return new RequestMappingInfo(Arrays.asList(annotation.value()), Arrays.asList(annotation.method()), RequestConditionFactory.parseParams(annotation.params()), - RequestConditionFactory.parseHeaders(annotation.headers())); + RequestConditionFactory.parseHeaders(annotation.headers()), + RequestConditionFactory.parseConsumes()); } @Override diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingInfo.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingInfo.java index 22b6963f88e..0cab731efab 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingInfo.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingInfo.java @@ -53,6 +53,8 @@ public final class RequestMappingInfo { private final RequestCondition headersCondition; + private final RequestCondition consumesCondition; + private int hash; /** @@ -61,7 +63,7 @@ public final class RequestMappingInfo { *

Package protected for testing purposes. */ RequestMappingInfo(Collection patterns, Collection methods) { - this(patterns, methods, null, null); + this(patterns, methods, null, null, null); } /** @@ -70,11 +72,13 @@ public final class RequestMappingInfo { public RequestMappingInfo(Collection patterns, Collection methods, RequestCondition paramsCondition, - RequestCondition headersCondition) { + RequestCondition headersCondition, + RequestCondition consumesCondition) { this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns)); this.methods = asUnmodifiableSet(methods); this.paramsCondition = paramsCondition != null ? paramsCondition : RequestConditionFactory.trueCondition(); this.headersCondition = headersCondition != null ? headersCondition : RequestConditionFactory.trueCondition(); + this.consumesCondition = consumesCondition != null ? consumesCondition : RequestConditionFactory.trueCondition(); } private static Set prependLeadingSlash(Collection patterns) { @@ -139,6 +143,7 @@ public final class RequestMappingInfo { *

  • HTTP methods are combined as union of all HTTP methods listed in both keys. *
  • Request parameter are combined into a logical AND. *
  • Request header are combined into a logical AND. + *
  • Consumes .. TODO * * @param methodKey the key to combine with * @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns @@ -149,8 +154,9 @@ public final class RequestMappingInfo { Set methods = union(this.methods, methodKey.methods); RequestCondition params = RequestConditionFactory.and(this.paramsCondition, methodKey.paramsCondition); RequestCondition headers = RequestConditionFactory.and(this.headersCondition, methodKey.headersCondition); + RequestCondition consumes = RequestConditionFactory.mostSpecific(methodKey.consumesCondition, this.consumesCondition); - return new RequestMappingInfo(patterns, methods, params, headers); + return new RequestMappingInfo(patterns, methods, params, headers, consumes); } private static Set combinePatterns(Collection typePatterns, @@ -197,14 +203,16 @@ public final class RequestMappingInfo { * @return a new request key that contains all matching attributes, or {@code null} if not all conditions match */ public RequestMappingInfo getMatchingRequestMapping(String lookupPath, HttpServletRequest request, PathMatcher pathMatcher) { - if (!checkMethod(request) || !paramsCondition.match(request) || !headersCondition.match(request)) { + if (!checkMethod(request) || !paramsCondition.match(request) || !headersCondition.match(request) || + !consumesCondition.match(request)) { return null; } else { List matchingPatterns = getMatchingPatterns(lookupPath, request, pathMatcher); if (!matchingPatterns.isEmpty()) { Set matchingMethods = getMatchingMethod(request); - return new RequestMappingInfo(matchingPatterns, matchingMethods, this.paramsCondition, this.headersCondition); + return new RequestMappingInfo(matchingPatterns, matchingMethods, this.paramsCondition, this.headersCondition, + this.consumesCondition); } else { return null; @@ -297,6 +305,7 @@ public final class RequestMappingInfo { } builder.append(",params=").append(paramsCondition.toString()); builder.append(",headers=").append(headersCondition.toString()); + builder.append(",consumes=").append(consumesCondition.toString()); builder.append('}'); return builder.toString(); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyComparatorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyComparatorTests.java index 1d199f02d10..bcc5b8a374f 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyComparatorTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyComparatorTests.java @@ -100,7 +100,7 @@ public class RequestKeyComparatorTests { RequestMappingInfo empty = new RequestMappingInfo(null, null); RequestMappingInfo oneMethod = new RequestMappingInfo(null, asList(RequestMethod.GET)); RequestMappingInfo oneMethodOneParam = - new RequestMappingInfo(null, asList(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null); + new RequestMappingInfo(null, asList(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null, null); List list = asList(empty, oneMethod, oneMethodOneParam); Collections.shuffle(list); Collections.sort(list, handlerMapping.getMappingComparator("", request)); @@ -113,8 +113,8 @@ public class RequestKeyComparatorTests { @Test @Ignore // TODO : remove ignore public void acceptHeaders() { - RequestMappingInfo html = new RequestMappingInfo(null, null, null, RequestConditionFactory.parseHeaders("accept=text/html")); - RequestMappingInfo xml = new RequestMappingInfo(null, null, null, RequestConditionFactory.parseHeaders("accept=application/xml")); + RequestMappingInfo html = new RequestMappingInfo(null, null, null, RequestConditionFactory.parseHeaders("accept=text/html"), null); + RequestMappingInfo xml = new RequestMappingInfo(null, null, null, RequestConditionFactory.parseHeaders("accept=application/xml"), null); RequestMappingInfo none = new RequestMappingInfo(null, null); request.addHeader("Accept", "application/xml, text/html"); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyTests.java index 2d9654ecc3c..d95b6c1c9cb 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestKeyTests.java @@ -179,12 +179,12 @@ public class RequestKeyTests { request.setParameter("foo", "bar"); String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key = new RequestMappingInfo(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null); + RequestMappingInfo key = new RequestMappingInfo(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null, null); RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); assertNotNull(match); - key = new RequestMappingInfo(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null); + key = new RequestMappingInfo(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null, null); match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); assertNull(match); @@ -197,36 +197,36 @@ public class RequestKeyTests { request.addHeader("foo", "bar"); String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); - RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar")); + RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"), null); RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); assertNotNull(match); - key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar")); + key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"), null); match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); assertNull(match); } -// @Test -// public void consumesCondition() { -// PathMatcher pathMatcher = new AntPathMatcher(); -// MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); -// request.setContentType("text/plain"); -// String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); -// -// RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null, RequestConditionFactory.parseConsumes( -// "text/plain")); -// RequestMappingInfo match = key.getMatchingKey(lookupPath, request, pathMatcher); -// -// assertNotNull(match); -// -// key = new RequestMappingInfo(singleton("/foo"), null, null, null, RequestConditionFactory.parseConsumes( -// "application/xml")); -// match = key.getMatchingKey(lookupPath, request, pathMatcher); -// -// assertNull(match); -// } + @Test + public void consumesCondition() { + PathMatcher pathMatcher = new AntPathMatcher(); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); + request.setContentType("text/plain"); + String lookupPath = new UrlPathHelper().getLookupPathForRequest(request); + + RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null, RequestConditionFactory.parseConsumes( + "text/plain")); + RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + + assertNotNull(match); + + key = new RequestMappingInfo(singleton("/foo"), null, null, null, RequestConditionFactory.parseConsumes( + "application/xml")); + match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher); + + assertNull(match); + } private RequestMappingInfo createKeyFromPatterns(String... patterns) { return new RequestMappingInfo(asList(patterns), null); diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index 91ce9516a05..4961759d90f 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -55,7 +55,7 @@ import java.lang.annotation.Target; * As a consequence, such an argument will never be null. * Note that session access may not be thread-safe, in particular in a * Servlet environment: Consider switching the - * {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#setSynchronizeOnSession "synchronizeOnSession"} + * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter#setSynchronizeOnSession "synchronizeOnSession"} * flag to "true" if multiple requests are allowed to access a session concurrently. *
  • {@link org.springframework.web.context.request.WebRequest} or * {@link org.springframework.web.context.request.NativeWebRequest}. @@ -106,7 +106,7 @@ import java.lang.annotation.Target; *
  • Command/form objects to bind parameters to: as bean properties or fields, * with customizable type conversion, depending on {@link InitBinder} methods * and/or the HandlerAdapter configuration - see the "webBindingInitializer" - * property on AnnotationMethodHandlerAdapter. + * property on RequestMappingHandlerMethodAdapter. * Such command objects along with their validation results will be exposed * as model attributes, by default using the non-qualified command class name * in property notation (e.g. "orderAddress" for type "mypackage.OrderAddress"). @@ -180,8 +180,8 @@ import java.lang.annotation.Target; * DispatcherServlet and DispatcherPortlet. * However, if you are defining custom HandlerMappings or * HandlerAdapters, then you need to make sure that a - * corresponding custom DefaultAnnotationHandlerMapping - * and/or AnnotationMethodHandlerAdapter is defined as well + * corresponding custom RequestMappingHandlerMethodMapping + * and/or RequestMappingHandlerMethodAdapter is defined as well * - provided that you intend to use @RequestMapping. * *

    NOTE: When using controller interfaces (e.g. for AOP proxying), @@ -198,8 +198,8 @@ import java.lang.annotation.Target; * @see SessionAttributes * @see InitBinder * @see org.springframework.web.context.request.WebRequest - * @see org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter + * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping + * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter * @see org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping * @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter */ @@ -297,6 +297,4 @@ public @interface RequestMapping { */ String[] headers() default {}; - String[] consumes() default "*/*"; - }