From 71aae405d57288e83086e36143df3eb9bd6dba39 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 10 May 2011 09:23:00 +0000 Subject: [PATCH] SPR-7354 - Added equivalent of JAX-RS @Consumes to Spring MVC --- .../RequestMappingHandlerMapping.java | 7 +- .../method/annotation/RequestMappingInfo.java | 75 +++--- .../condition/AbstractNameValueCondition.java | 7 +- .../condition/AbstractRequestCondition.java | 41 ---- .../condition/ConsumesRequestCondition.java | 185 ++++++++++++--- .../condition/HeaderRequestCondition.java | 64 ----- .../condition/HeadersRequestCondition.java | 134 +++++++++++ .../LogicalConjunctionRequestCondition.java | 14 +- .../LogicalDisjunctionRequestCondition.java | 8 +- .../LogicalNegationRequestCondition.java | 73 ------ .../MediaTypeHeaderRequestCondition.java | 73 ------ .../condition/ParamRequestCondition.java | 66 ------ .../condition/ParamsRequestCondition.java | 126 ++++++++++ .../method/condition/RequestCondition.java | 2 +- .../condition/RequestConditionComposite.java | 29 +-- .../condition/RequestConditionFactory.java | 220 +++++------------- .../ConsumesRequestConditionTests.java | 192 +++++++++++++++ .../HeadersRequestConditionTests.java | 150 ++++++++++++ .../ParamsRequestConditionTests.java | 129 ++++++++++ .../RequestConditionFactoryTests.java | 212 ----------------- .../web/bind/annotation/RequestMapping.java | 16 +- 21 files changed, 1031 insertions(+), 792 deletions(-) delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeaderRequestCondition.java create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalNegationRequestCondition.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeHeaderRequestCondition.java delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamRequestCondition.java create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java delete mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactoryTests.java diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 4365ea15524..43a63e5c4da 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -24,7 +24,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; - import javax.servlet.http.HttpServletRequest; import org.springframework.core.annotation.AnnotationUtils; @@ -128,7 +127,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping methods; - private final RequestCondition paramsCondition; + private final ParamsRequestCondition paramsCondition; - private final RequestCondition headersCondition; + private final HeadersRequestCondition headersCondition; - private final RequestCondition consumesCondition; + private final ConsumesRequestCondition consumesCondition; private int hash; /** - * Creates a new {@code RequestKey} instance with the given URL patterns and HTTP methods. + * Creates a new {@code RequestMappingInfo} instance with the given URL patterns and HTTP methods. * *

Package protected for testing purposes. */ @@ -67,18 +67,18 @@ public final class RequestMappingInfo { } /** - * Creates a new {@code RequestKey} instance with a full set of conditions. + * Creates a new {@code RequestMappingInfo} instance with a full set of conditions. */ public RequestMappingInfo(Collection patterns, Collection methods, - RequestCondition paramsCondition, - RequestCondition headersCondition, - RequestCondition consumesCondition) { + ParamsRequestCondition paramsCondition, + HeadersRequestCondition headersCondition, + ConsumesRequestCondition 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(); + this.paramsCondition = paramsCondition != null ? paramsCondition : new ParamsRequestCondition(); + this.headersCondition = headersCondition != null ? headersCondition : new HeadersRequestCondition(); + this.consumesCondition = consumesCondition != null ? consumesCondition : new ConsumesRequestCondition(); } private static Set prependLeadingSlash(Collection patterns) { @@ -118,21 +118,28 @@ public final class RequestMappingInfo { } /** - * Returns the request parameters of this request key. + * Returns the request parameters conditions of this request key. */ - public RequestCondition getParams() { + public ParamsRequestCondition getParams() { return paramsCondition; } /** - * Returns the request headers of this request key. + * Returns the request headers conditions of this request key. */ - public RequestCondition getHeaders() { + public HeadersRequestCondition getHeaders() { return headersCondition; } /** - * Combines this {@code RequestKey} with another as follows: + * Returns the request consumes conditions of this request key. + */ + public ConsumesRequestCondition getConsumes() { + return consumesCondition; + } + + /** + * Combines this {@code RequestMappingInfo} with another as follows: *

* @param methodKey the key to combine with * @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns @@ -152,9 +159,9 @@ public final class RequestMappingInfo { public RequestMappingInfo combine(RequestMappingInfo methodKey, PathMatcher pathMatcher) { Set patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher); 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); + ParamsRequestCondition params = this.paramsCondition.combine(methodKey.paramsCondition); + HeadersRequestCondition headers = this.headersCondition.combine(methodKey.headersCondition); + ConsumesRequestCondition consumes = this.consumesCondition.combine(methodKey.consumesCondition); return new RequestMappingInfo(patterns, methods, params, headers, consumes); } @@ -189,7 +196,7 @@ public final class RequestMappingInfo { } /** - * Returns a new {@code RequestKey} that contains all conditions of this key that are relevant to the request. + * Returns a new {@code RequestMappingInfo} that contains all conditions of this key that are relevant to the request. *
    *
  • The list of URL path patterns is trimmed to contain the patterns that match the URL with matching patterns * sorted via {@link PathMatcher#getPatternComparator(String)}. @@ -203,16 +210,20 @@ 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) || - !consumesCondition.match(request)) { + ParamsRequestCondition matchingParamsCondition = paramsCondition.getMatchingCondition(request); + HeadersRequestCondition matchingHeadersCondition = headersCondition.getMatchingCondition(request); + ConsumesRequestCondition matchingConsumesCondition = consumesCondition.getMatchingCondition(request); + + if (!checkMethod(request) || matchingParamsCondition == null || matchingHeadersCondition == null || + matchingConsumesCondition == null) { 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, - this.consumesCondition); + return new RequestMappingInfo(matchingPatterns, matchingMethods, matchingParamsCondition, + matchingHeadersCondition, matchingConsumesCondition); } else { return null; @@ -277,7 +288,8 @@ public final class RequestMappingInfo { RequestMappingInfo other = (RequestMappingInfo) obj; return (this.patterns.equals(other.patterns) && this.methods.equals(other.methods) && this.paramsCondition.equals(other.paramsCondition) && - this.headersCondition.equals(other.headersCondition)); + this.headersCondition.equals(other.headersCondition) && + this.consumesCondition.equals(other.consumesCondition)); } return false; } @@ -290,6 +302,7 @@ public final class RequestMappingInfo { result = 31 * result + methods.hashCode(); result = 31 * result + paramsCondition.hashCode(); result = 31 * result + headersCondition.hashCode(); + result = 31 * result + consumesCondition.hashCode(); hash = result; } return result; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java index 74b20085c16..ea859b7039e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest; * @author Arjen Poutsma * @since 3.1 */ -abstract class AbstractNameValueCondition extends AbstractRequestCondition { +abstract class AbstractNameValueCondition implements RequestCondition { protected final String name; @@ -65,11 +65,6 @@ abstract class AbstractNameValueCondition extends AbstractRequestCondition { protected abstract boolean matchValue(HttpServletRequest request); - @Override - public int getSpecificity() { - return 1; - } - @Override public int hashCode() { int result = name.hashCode(); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java deleted file mode 100644 index 5522aec7860..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractRequestCondition.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2011 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 - * - * http://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.web.servlet.mvc.method.condition; - -/** - * Abstract base class for {@link RequestCondition} that provides a standard {@link Comparable} implementation based on - * the conditions {@linkplain #getSpecificity() specificity}. - * - * @author Arjen Poutsma - * @since 3.1 - */ -public abstract class AbstractRequestCondition implements RequestCondition { - - /** - * Returns the conditions specificity. More specific conditions should return a higher value than ones which are less - * so. - * - * @return the conditions specificity - */ - protected abstract int getSpecificity(); - - public int compareTo(RequestCondition o) { - AbstractRequestCondition other = (AbstractRequestCondition) o; - return other.getSpecificity() - this.getSpecificity(); - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java index 5e9cf0d9932..0244584f08a 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java @@ -16,57 +16,186 @@ package org.springframework.web.servlet.mvc.method.condition; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** + * Represents a collection of consumes conditions, typically obtained from {@link + * org.springframework.web.bind.annotation.RequestMapping#consumes() @RequestMapping.consumes()}. + * * @author Arjen Poutsma + * @see RequestConditionFactory#parseHeaders(String...) + * @since 3.1 */ -class ConsumesRequestCondition extends AbstractRequestCondition { +public class ConsumesRequestCondition + extends LogicalDisjunctionRequestCondition + implements Comparable { - private final MediaType mediaType; + private final ConsumeRequestCondition mostSpecificCondition; - ConsumesRequestCondition(String mediaType) { - this.mediaType = MediaType.parseMediaType(mediaType); + ConsumesRequestCondition(Collection conditions) { + super(conditions); + Assert.notEmpty(conditions, "'conditions' must not be empty"); + mostSpecificCondition = getMostSpecificCondition(); } - public boolean match(HttpServletRequest request) { - MediaType contentType; - if (StringUtils.hasLength(request.getContentType())) { - contentType = MediaType.parseMediaType(request.getContentType()); + private ConsumeRequestCondition getMostSpecificCondition() { + List conditions = new ArrayList(getConditions()); + Collections.sort(conditions); + return conditions.get(0); + } + + ConsumesRequestCondition(String... consumes) { + this(parseConditions(Arrays.asList(consumes))); + } + + private static Set parseConditions(List consumes) { + if (consumes.isEmpty()) { + consumes = Collections.singletonList("*/*"); + } + Set conditions = new LinkedHashSet(consumes.size()); + for (String consume : consumes) { + conditions.add(new ConsumeRequestCondition(consume)); + } + return conditions; + } + + /** + * Creates an default set of consumes request conditions. + */ + public ConsumesRequestCondition() { + this(Collections.singleton(new ConsumeRequestCondition(MediaType.ALL, false))); + } + + /** + * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request. + * + * @param request the request + * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match + */ + public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) { + Set matchingConditions = new LinkedHashSet(getConditions()); + for (Iterator iterator = matchingConditions.iterator(); iterator.hasNext();) { + ConsumeRequestCondition condition = iterator.next(); + if (!condition.match(request)) { + iterator.remove(); + } + } + if (matchingConditions.isEmpty()) { + return null; } else { - contentType = MediaType.APPLICATION_OCTET_STREAM; + return new ConsumesRequestCondition(matchingConditions); } - return this.mediaType.includes(contentType); } - @Override - public int getSpecificity() { - return 1; + /** + * Combines this collection of request condition with another. Returns {@code other}, unless it has the default + * value (i.e. */*). + * + * @param other the condition to combine with + */ + public ConsumesRequestCondition combine(ConsumesRequestCondition other) { + return !other.hasDefaultValue() ? other : this; } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; + private boolean hasDefaultValue() { + Set conditions = getConditions(); + if (conditions.size() == 1) { + ConsumeRequestCondition condition = conditions.iterator().next(); + return condition.getMediaType().equals(MediaType.ALL); } - if (obj != null && obj instanceof ConsumesRequestCondition) { - ConsumesRequestCondition other = (ConsumesRequestCondition) obj; - return this.mediaType.equals(other.mediaType); + else { + return false; } - return false; } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (mediaType != null) { + public int compareTo(ConsumesRequestCondition other) { + return this.mostSpecificCondition.compareTo(other.mostSpecificCondition); + } + + private static MediaType getContentType(HttpServletRequest request) { + if (StringUtils.hasLength(request.getContentType())) { + return MediaType.parseMediaType(request.getContentType()); + } + else { + return MediaType.APPLICATION_OCTET_STREAM; + } + } + + static class ConsumeRequestCondition implements RequestCondition, Comparable { + + private final MediaType mediaType; + + private final boolean isNegated; + + ConsumeRequestCondition(String expression) { + if (expression.startsWith("!")) { + isNegated = true; + expression = expression.substring(1); + } + else { + isNegated = false; + } + this.mediaType = MediaType.parseMediaType(expression); + } + + ConsumeRequestCondition(MediaType mediaType, boolean isNegated) { + this.mediaType = mediaType; + this.isNegated = isNegated; + } + + public boolean match(HttpServletRequest request) { + MediaType contentType = getContentType(request); + boolean match = this.mediaType.includes(contentType); + return !isNegated ? match : !match; + } + + public int compareTo(ConsumeRequestCondition other) { + return MediaType.SPECIFICITY_COMPARATOR.compare(this.mediaType, other.mediaType); + } + + MediaType getMediaType() { + return mediaType; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj instanceof ConsumeRequestCondition) { + ConsumeRequestCondition other = (ConsumeRequestCondition) obj; + return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated); + } + return false; + } + + @Override + public int hashCode() { + return mediaType.hashCode(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (isNegated) { + builder.append('!'); + } builder.append(mediaType.toString()); + return builder.toString(); } - return builder.toString(); + } - -} \ No newline at end of file +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeaderRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeaderRequestCondition.java deleted file mode 100644 index e8dc4461e06..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeaderRequestCondition.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2002-2011 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 - * - * http://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.web.servlet.mvc.method.condition; - -import javax.servlet.http.HttpServletRequest; - -/** - * Request header name-value condition. - * - * @author Rossen Stoyanchev - * @author Arjen Poutsma - * @see org.springframework.web.bind.annotation.RequestMapping#headers() - * @since 3.1 - */ -class HeaderRequestCondition extends AbstractNameValueCondition { - - public HeaderRequestCondition(String expression) { - super(expression); - } - - @Override - protected String parseValue(String valueExpression) { - return valueExpression; - } - - @Override - protected boolean matchName(HttpServletRequest request) { - return request.getHeader(name) != null; - } - - @Override - protected boolean matchValue(HttpServletRequest request) { - return value.equals(request.getHeader(name)); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof HeaderRequestCondition) { - HeaderRequestCondition other = (HeaderRequestCondition) obj; - return ((this.name.equalsIgnoreCase(other.name)) && - (this.value != null ? this.value.equals(other.value) : other.value == null) && - this.isNegated == other.isNegated); - } - return false; - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java new file mode 100644 index 00000000000..fbe4bdfa786 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2011 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 + * + * http://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.web.servlet.mvc.method.condition; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; + +/** + * Represents a collection of header request conditions, typically obtained from {@link + * org.springframework.web.bind.annotation.RequestMapping#headers() @RequestMapping.headers()}. + * + * @author Arjen Poutsma + * @see RequestConditionFactory#parseHeaders(String...) + * @since 3.1 + */ +public class HeadersRequestCondition + extends LogicalConjunctionRequestCondition + implements Comparable { + + HeadersRequestCondition(Collection conditions) { + super(conditions); + } + + HeadersRequestCondition(String... headers) { + this(parseConditions(Arrays.asList(headers))); + } + + private static Set parseConditions(Collection params) { + Set conditions = new LinkedHashSet(params.size()); + for (String param : params) { + conditions.add(new HeaderRequestCondition(param)); + } + return conditions; + } + + /** + * Creates an empty set of header request conditions. + */ + public HeadersRequestCondition() { + this(Collections.emptySet()); + } + + /** + * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request. + * + * @param request the request + * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match + */ + public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) { + return match(request) ? this : null; + } + + + /** + * Combines this collection of request condition with another by combining all header request conditions into a + * logical AND. + * + * @param other the condition to combine with + */ + public HeadersRequestCondition combine(HeadersRequestCondition other) { + Set conditions = new LinkedHashSet(getConditions()); + conditions.addAll(other.getConditions()); + return new HeadersRequestCondition(conditions); + } + + + public int compareTo(HeadersRequestCondition other) { + return other.getConditions().size() - this.getConditions().size(); + } + + static class HeaderRequestCondition extends AbstractNameValueCondition { + + public HeaderRequestCondition(String expression) { + super(expression); + } + + @Override + protected String parseValue(String valueExpression) { + return valueExpression; + } + + @Override + protected boolean matchName(HttpServletRequest request) { + return request.getHeader(name) != null; + } + + @Override + protected boolean matchValue(HttpServletRequest request) { + return value.equals(request.getHeader(name)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj instanceof HeaderRequestCondition) { + HeaderRequestCondition other = (HeaderRequestCondition) obj; + return ((this.name.equalsIgnoreCase(other.name)) && + (this.value != null ? this.value.equals(other.value) : other.value == null) && + this.isNegated == other.isNegated); + } + return false; + } + + @Override + public int hashCode() { + int result = name.toLowerCase().hashCode(); + result = 31 * result + (value != null ? value.hashCode() : 0); + result = 31 * result + (isNegated ? 1 : 0); + return result; + } + + + } +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java index c03818956f9..fd8058b3f2e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java @@ -16,7 +16,8 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.List; +import java.util.Collection; +import java.util.Set; import javax.servlet.http.HttpServletRequest; /** @@ -25,14 +26,18 @@ import javax.servlet.http.HttpServletRequest; * @author Arjen Poutsma * @since 3.1 */ -class LogicalConjunctionRequestCondition extends RequestConditionComposite { +class LogicalConjunctionRequestCondition extends RequestConditionComposite { - LogicalConjunctionRequestCondition(List conditions) { + LogicalConjunctionRequestCondition(Collection conditions) { super(conditions); } public boolean match(HttpServletRequest request) { - for (RequestCondition condition : conditions) { + Set conditions = getConditions(); + if (conditions.isEmpty()) { + return true; + } + for (T condition : conditions) { if (!condition.match(request)) { return false; } @@ -40,6 +45,7 @@ class LogicalConjunctionRequestCondition extends RequestConditionComposite { return true; } + @Override protected String getToStringInfix() { return " && "; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java index 6c45f68ecc0..a4e5e653c57 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java @@ -16,7 +16,7 @@ package org.springframework.web.servlet.mvc.method.condition; -import java.util.List; +import java.util.Collection; import javax.servlet.http.HttpServletRequest; /** @@ -25,14 +25,14 @@ import javax.servlet.http.HttpServletRequest; * @author Arjen Poutsma * @since 3.1 */ -class LogicalDisjunctionRequestCondition extends RequestConditionComposite { +class LogicalDisjunctionRequestCondition extends RequestConditionComposite { - LogicalDisjunctionRequestCondition(List conditions) { + LogicalDisjunctionRequestCondition(Collection conditions) { super(conditions); } public boolean match(HttpServletRequest request) { - for (RequestCondition condition : conditions) { + for (RequestCondition condition : getConditions()) { if (condition.match(request)) { return true; } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalNegationRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalNegationRequestCondition.java deleted file mode 100644 index f5d4cb2ea74..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalNegationRequestCondition.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2011 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 - * - * http://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.web.servlet.mvc.method.condition; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.util.Assert; - -/** - * {@link RequestCondition} implementation that represents a logical NOT (i.e. !). - * - * @author Arjen Poutsma - * @since 3.1 - */ -class LogicalNegationRequestCondition extends AbstractRequestCondition { - - private final RequestCondition requestCondition; - - LogicalNegationRequestCondition(RequestCondition requestCondition) { - Assert.notNull(requestCondition, "'requestCondition' must not be null"); - this.requestCondition = requestCondition; - } - - @Override - protected int getSpecificity() { - if (requestCondition instanceof AbstractRequestCondition) { - return ((AbstractRequestCondition) requestCondition).getSpecificity(); - } - else { - return 0; - } - } - - public boolean match(HttpServletRequest request) { - return !requestCondition.match(request); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o != null && o instanceof LogicalNegationRequestCondition) { - LogicalNegationRequestCondition other = (LogicalNegationRequestCondition) o; - return this.requestCondition.equals(other.requestCondition); - } - return false; - } - - @Override - public int hashCode() { - return requestCondition.hashCode(); - } - - @Override - public String toString() { - return "!(" + requestCondition.toString() + ")"; - } -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeHeaderRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeHeaderRequestCondition.java deleted file mode 100644 index cec3cae3c6f..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeHeaderRequestCondition.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2011 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 - * - * http://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.web.servlet.mvc.method.condition; - -import java.util.Collections; -import java.util.List; -import javax.servlet.http.HttpServletRequest; - -import org.springframework.http.MediaType; - -/** - * A RequestCondition that for headers that contain {@link org.springframework.http.MediaType MediaTypes}. - */ -class MediaTypeHeaderRequestCondition extends AbstractNameValueCondition> { - - public MediaTypeHeaderRequestCondition(String expression) { - super(expression); - } - - @Override - protected List parseValue(String valueExpression) { - return Collections.unmodifiableList(MediaType.parseMediaTypes(valueExpression)); - } - - @Override - protected boolean matchName(HttpServletRequest request) { - return request.getHeader(name) != null; - } - - @Override - protected boolean matchValue(HttpServletRequest request) { - List requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(name)); - - for (MediaType mediaType : this.value) { - for (MediaType requestMediaType : requestMediaTypes) { - if (mediaType.includes(requestMediaType)) { - return true; - } - } - } - return false; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof MediaTypeHeaderRequestCondition) { - MediaTypeHeaderRequestCondition other = (MediaTypeHeaderRequestCondition) obj; - return ((this.name.equalsIgnoreCase(other.name)) && - (this.value != null ? this.value.equals(other.value) : other.value == null) && - this.isNegated == other.isNegated); - } - return false; - } - - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamRequestCondition.java deleted file mode 100644 index c3d1761627f..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamRequestCondition.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2011 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 - * - * http://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.web.servlet.mvc.method.condition; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.web.util.WebUtils; - -/** - * Request parameter name-value condition. - * - * @author Rossen Stoyanchev - * @author Arjen Poutsma - * @see org.springframework.web.bind.annotation.RequestMapping#params() - * @since 3.1 - */ -class ParamRequestCondition extends AbstractNameValueCondition { - - ParamRequestCondition(String expression) { - super(expression); - } - - @Override - protected String parseValue(String valueExpression) { - return valueExpression; - } - - @Override - protected boolean matchName(HttpServletRequest request) { - return WebUtils.hasSubmitParameter(request, name); - } - - @Override - protected boolean matchValue(HttpServletRequest request) { - return value.equals(request.getParameter(name)); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof ParamRequestCondition) { - ParamRequestCondition other = (ParamRequestCondition) obj; - return ((this.name.equals(other.name)) && - (this.value != null ? this.value.equals(other.value) : other.value == null) && - this.isNegated == other.isNegated); - } - return false; - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java new file mode 100644 index 00000000000..e3f6b5fb940 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002-2011 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 + * + * http://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.web.servlet.mvc.method.condition; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.util.WebUtils; + +/** + * Represents a collection of parameter request conditions, typically obtained from {@link + * org.springframework.web.bind.annotation.RequestMapping#params() @RequestMapping.params()}. + * + * @author Arjen Poutsma + * @see RequestConditionFactory#parseParams(String...) + * @since 3.1 + */ +public class ParamsRequestCondition + extends LogicalConjunctionRequestCondition + implements Comparable { + + private ParamsRequestCondition(Collection conditions) { + super(conditions); + } + + ParamsRequestCondition(String... params) { + this(parseConditions(Arrays.asList(params))); + } + + private static Set parseConditions(List params) { + Set conditions = new LinkedHashSet(params.size()); + for (String param : params) { + conditions.add(new ParamRequestCondition(param)); + } + return conditions; + } + + /** + * Creates an empty set of parameter request conditions. + */ + public ParamsRequestCondition() { + this(Collections.emptySet()); + } + + /** + * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request. + * + * @param request the request + * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match + */ + public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) { + return match(request) ? this : null; + } + + /** + * Combines this collection of request condition with another by combining all parameter request conditions into a + * logical AND. + * + * @param other the condition to combine with + */ + public ParamsRequestCondition combine(ParamsRequestCondition other) { + Set conditions = new LinkedHashSet(getConditions()); + conditions.addAll(other.getConditions()); + return new ParamsRequestCondition(conditions); + } + + public int compareTo(ParamsRequestCondition other) { + return other.getConditions().size() - this.getConditions().size(); + } + + static class ParamRequestCondition extends AbstractNameValueCondition { + + ParamRequestCondition(String expression) { + super(expression); + } + + @Override + protected String parseValue(String valueExpression) { + return valueExpression; + } + + @Override + protected boolean matchName(HttpServletRequest request) { + return WebUtils.hasSubmitParameter(request, name); + } + + @Override + protected boolean matchValue(HttpServletRequest request) { + return value.equals(request.getParameter(name)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj instanceof ParamRequestCondition) { + ParamRequestCondition other = (ParamRequestCondition) obj; + return ((this.name.equals(other.name)) && + (this.value != null ? this.value.equals(other.value) : other.value == null) && + this.isNegated == other.isNegated); + } + return false; + } + + } +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java index ada88f45751..ee4ac36c1a5 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java @@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletRequest; * @see RequestConditionFactory * @since 3.1 */ -public interface RequestCondition extends Comparable { +public interface RequestCondition { /** * Indicates whether this condition matches against the given servlet request. diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java index f060d615ced..7d1816bb42c 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java @@ -16,11 +16,11 @@ package org.springframework.web.servlet.mvc.method.condition; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.List; - -import org.springframework.util.Assert; +import java.util.LinkedHashSet; +import java.util.Set; /** * Abstract base class for {@link RequestCondition} implementations that wrap other request conditions. @@ -28,25 +28,16 @@ import org.springframework.util.Assert; * @author Arjen Poutsma * @since 3.1 */ -abstract class RequestConditionComposite extends AbstractRequestCondition { +abstract class RequestConditionComposite implements RequestCondition { - protected final List conditions; + private final Set conditions; - protected RequestConditionComposite(List conditions) { - Assert.notEmpty(conditions, "'conditions' must not be empty"); - this.conditions = Collections.unmodifiableList(conditions); + protected RequestConditionComposite(Collection conditions) { + this.conditions = Collections.unmodifiableSet(new LinkedHashSet(conditions)); } - @Override - public int getSpecificity() { - int weight = 0; - for (RequestCondition condition : conditions) { - if (condition instanceof AbstractRequestCondition) { - AbstractRequestCondition abstractRequestCondition = (AbstractRequestCondition) condition; - weight += abstractRequestCondition.getSpecificity(); - } - } - return weight; + protected Set getConditions() { + return conditions; } @Override @@ -70,7 +61,7 @@ abstract class RequestConditionComposite extends AbstractRequestCondition { public String toString() { StringBuilder builder = new StringBuilder("["); String infix = getToStringInfix(); - for (Iterator iterator = conditions.iterator(); iterator.hasNext();) { + for (Iterator iterator = conditions.iterator(); iterator.hasNext();) { RequestCondition condition = iterator.next(); builder.append(condition.toString()); if (iterator.hasNext()) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java index 9ef20b38b23..f05a9428b22 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java @@ -17,13 +17,12 @@ package org.springframework.web.servlet.mvc.method.condition; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; -import javax.servlet.http.HttpServletRequest; +import java.util.Set; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; +import org.springframework.http.MediaType; /** * Factory for {@link RequestCondition} objects. @@ -34,132 +33,9 @@ import org.springframework.util.StringUtils; */ public abstract class RequestConditionFactory { - private static final RequestCondition TRUE_CONDITION = new AbstractRequestCondition() { - public boolean match(HttpServletRequest request) { - return true; - } + private static final String CONTENT_TYPE_HEADER = "Content-Type"; - @Override - public int getSpecificity() { - return 0; - } - - @Override - public String toString() { - return "TRUE"; - } - }; - - private static final RequestCondition FALSE_CONDITION = new AbstractRequestCondition() { - public boolean match(HttpServletRequest request) { - return false; - } - - @Override - public int getSpecificity() { - return 0; - } - - @Override - public String toString() { - return "FALSE"; - } - }; - - /** - * Returns a condition that always returns {@code true} for {@link RequestCondition#match(HttpServletRequest)}. - * - * @return a condition that returns {@code true} - */ - public static RequestCondition trueCondition() { - return TRUE_CONDITION; - } - - /** - * Returns a condition that always returns {@code false} for {@link RequestCondition#match(HttpServletRequest)}. - * - * @return a condition that returns {@code false} - */ - public static RequestCondition falseCondition() { - return FALSE_CONDITION; - } - - public static RequestCondition mostSpecific(RequestCondition... conditions) { - if (ObjectUtils.isEmpty(conditions)) { - return trueCondition(); - } - RequestCondition[] copy = new RequestCondition[conditions.length]; - System.arraycopy(conditions, 0, copy, 0, conditions.length); - Arrays.sort(copy); - return copy[0]; - } - - /** - * Wraps the given condition in a logical NOT, i.e. the returned condition will return {@code true} for {@link - * RequestCondition#match(HttpServletRequest)} if the given condition return {@code false}, and vice-versa. - * - * @return a condition that represents a logical NOT - */ - public static RequestCondition not(RequestCondition condition) { - if (condition == TRUE_CONDITION) { - return falseCondition(); - } - else if (condition == FALSE_CONDITION) { - return trueCondition(); - } - else { - return new LogicalNegationRequestCondition(condition); - } - } - - /** - * Combines the given conditions into a logical AND, i.e. the returned condition will return {@code true} for {@link - * RequestCondition#match(HttpServletRequest)} if all of the given conditions do so. - * - * @param conditions the conditions - * @return a condition that represents a logical AND - */ - public static RequestCondition and(RequestCondition... conditions) { - List filteredConditions = new ArrayList(Arrays.asList(conditions)); - for (Iterator iterator = filteredConditions.iterator(); iterator.hasNext();) { - RequestCondition condition = iterator.next(); - if (condition == TRUE_CONDITION) { - iterator.remove(); - } - } - if (filteredConditions.isEmpty()) { - return trueCondition(); - } - else { - return new LogicalConjunctionRequestCondition(filteredConditions); - } - } - - /** - * Combines the given conditions into a logical OR, i.e. the returned condition will return {@code true} for {@link - * RequestCondition#match(HttpServletRequest)} if any of the given conditions do so. - * - * @param conditions the conditions - * @return a condition that represents a logical OR - */ - public static RequestCondition or(RequestCondition... conditions) { - List filteredConditions = new ArrayList(Arrays.asList(conditions)); - for (Iterator iterator = filteredConditions.iterator(); iterator.hasNext();) { - RequestCondition condition = iterator.next(); - if (condition == TRUE_CONDITION) { - return trueCondition(); - } - else if (condition == FALSE_CONDITION) { - iterator.remove(); - } - } - if (filteredConditions.isEmpty()) { - return trueCondition(); - } - else { - return new LogicalDisjunctionRequestCondition(filteredConditions); - } - } + private static final String ACCEPT_HEADER = "Accept"; /** * Parses the given parameters, and returns them as a single request conditions. @@ -168,15 +44,8 @@ public abstract class RequestConditionFactory { * @return the request condition * @see org.springframework.web.bind.annotation.RequestMapping#params() */ - public static RequestCondition parseParams(String... params) { - if (ObjectUtils.isEmpty(params)) { - return trueCondition(); - } - RequestCondition[] result = new RequestCondition[params.length]; - for (int i = 0; i < params.length; i++) { - result[i] = new ParamRequestCondition(params[i]); - } - return and(result); + public static ParamsRequestCondition parseParams(String... params) { + return new ParamsRequestCondition(params); } /** @@ -186,29 +55,22 @@ public abstract class RequestConditionFactory { * @return the request condition * @see org.springframework.web.bind.annotation.RequestMapping#headers() */ - public static RequestCondition parseHeaders(String... headers) { - if (ObjectUtils.isEmpty(headers)) { - return trueCondition(); - } - RequestCondition[] result = new RequestCondition[headers.length]; - for (int i = 0; i < headers.length; i++) { - HeaderRequestCondition header = new HeaderRequestCondition(headers[i]); - if ("Content-Type".equalsIgnoreCase(header.name) && StringUtils.hasLength(header.value)) { - RequestCondition consumesCondition = new ConsumesRequestCondition(header.value); - result[i] = header.isNegated ? not(consumesCondition) : consumesCondition; - } - else if (isMediaTypeHeader(header.name)) { - result[i] = new MediaTypeHeaderRequestCondition(headers[i]); - } - else { - result[i] = header; - } - } - return and(result); - } + public static HeadersRequestCondition parseHeaders(String... headers) { + HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers); - private static boolean isMediaTypeHeader(String name) { - return "Accept".equalsIgnoreCase(name) || "Content-Type".equalsIgnoreCase(name); + // filter out Accept and Content-Type headers, they are dealt with by produces and consumes respectively + Set filteredConditions = + new LinkedHashSet(headersCondition.getConditions()); + + for (Iterator iterator = filteredConditions.iterator(); + iterator.hasNext();) { + HeadersRequestCondition.HeaderRequestCondition headerCondition = iterator.next(); + if (ACCEPT_HEADER.equalsIgnoreCase(headerCondition.name) || + CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) { + iterator.remove(); + } + } + return new HeadersRequestCondition(filteredConditions); } /** @@ -218,15 +80,39 @@ public abstract class RequestConditionFactory { * @return the request condition * @see org.springframework.web.bind.annotation.RequestMapping#consumes() */ - public static RequestCondition parseConsumes(String... consumes) { - if (ObjectUtils.isEmpty(consumes)) { - return trueCondition(); + public static ConsumesRequestCondition parseConsumes(String... consumes) { + return new ConsumesRequestCondition(consumes); + } + + public static ConsumesRequestCondition parseConsumes(String[] consumes, String[] headers) { + + List allConditions = parseContentTypeHeaders(headers); + + // ignore the default consumes() value if any content-type headers have been set + boolean headersHasContentType = !allConditions.isEmpty(); + boolean consumesHasDefaultValue = consumes.length == 1 && consumes[0].equals("*/*"); + if (!headersHasContentType || !consumesHasDefaultValue) { + for (String consume : consumes) { + allConditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(consume)); + } } - RequestCondition[] result = new RequestCondition[consumes.length]; - for (int i = 0; i < consumes.length; i++) { - result[i] = new ConsumesRequestCondition(consumes[i]); + return new ConsumesRequestCondition(allConditions); + } + + private static List parseContentTypeHeaders(String[] headers) { + List allConditions = + new ArrayList(); + HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers); + for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) { + if (CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) { + List mediaTypes = MediaType.parseMediaTypes(headerCondition.value); + for (MediaType mediaType : mediaTypes) { + allConditions.add( + new ConsumesRequestCondition.ConsumeRequestCondition(mediaType, headerCondition.isNegated)); + } + } } - return or(result); + return allConditions; } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java new file mode 100644 index 00000000000..d6036d66bf1 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java @@ -0,0 +1,192 @@ +/* + * Copyright 2002-2011 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 + * + * http://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.web.servlet.mvc.method.condition; + +import java.util.Set; + +import org.junit.Test; + +import org.springframework.mock.web.MockHttpServletRequest; + +import static org.junit.Assert.*; + +/** + * @author Arjen Poutsma + */ +public class ConsumesRequestConditionTests { + + @Test + public void consumesMatch() { + RequestCondition condition = new ConsumesRequestCondition("text/plain"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("text/plain"); + + assertTrue(condition.match(request)); + } + + @Test + public void negatedConsumesMatch() { + RequestCondition condition = new ConsumesRequestCondition("!text/plain"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("text/plain"); + + assertFalse(condition.match(request)); + } + + @Test + public void consumesWildcardMatch() { + RequestCondition condition = new ConsumesRequestCondition("text/*"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("text/plain"); + + assertTrue(condition.match(request)); + } + + @Test + public void consumesMultipleMatch() { + RequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("text/plain"); + + assertTrue(condition.match(request)); + } + + @Test + public void consumesSingleNoMatch() { + RequestCondition condition = new ConsumesRequestCondition("text/plain"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("application/xml"); + + assertFalse(condition.match(request)); + } + + @Test + public void compareToSingle() { + ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain"); + ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*"); + + int result = condition1.compareTo(condition2); + assertTrue("Invalid comparison result: " + result, result < 0); + + result = condition2.compareTo(condition1); + assertTrue("Invalid comparison result: " + result, result > 0); + } + + @Test + public void compareToMultiple() { + ConsumesRequestCondition condition1 = new ConsumesRequestCondition("*/*", "text/plain"); + ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*", "text/plain;q=0.7"); + + int result = condition1.compareTo(condition2); + assertTrue("Invalid comparison result: " + result, result < 0); + + result = condition2.compareTo(condition1); + assertTrue("Invalid comparison result: " + result, result > 0); + } + + + @Test + public void combine() { + ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain"); + ConsumesRequestCondition condition2 = new ConsumesRequestCondition("application/xml"); + + ConsumesRequestCondition result = condition1.combine(condition2); + assertEquals(condition2, result); + } + + @Test + public void combineWithDefault() { + ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain"); + ConsumesRequestCondition condition2 = new ConsumesRequestCondition("*/*"); + + ConsumesRequestCondition result = condition1.combine(condition2); + assertEquals(condition1, result); + } + + @Test + public void parseConsumesAndHeaders() { + String[] consumes = new String[] {"text/plain"}; + String[] headers = new String[]{"foo=bar", "content-type=application/xml,application/pdf"}; + ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers); + + assertConditions(condition, "text/plain", "application/xml", "application/pdf"); + } + + @Test + public void parseConsumesDefault() { + String[] consumes = new String[] {"*/*"}; + String[] headers = new String[0]; + ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers); + + assertConditions(condition, "*/*"); + } + @Test + public void parseConsumesDefaultAndHeaders() { + String[] consumes = new String[] {"*/*"}; + String[] headers = new String[]{"foo=bar", "content-type=text/plain"}; + ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers); + + assertConditions(condition, "text/plain"); + } + + @Test + public void getMatchingCondition() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContentType("text/plain"); + + ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml"); + + ConsumesRequestCondition result = condition.getMatchingCondition(request); + assertConditions(result, "text/plain"); + + condition = new ConsumesRequestCondition("application/xml"); + + result = condition.getMatchingCondition(request); + assertNull(result); + } + + private void assertConditions(ConsumesRequestCondition condition, String... expected) { + Set conditions = condition.getConditions(); + assertEquals("Invalid amount of conditions", conditions.size(), expected.length); + for (String s : expected) { + boolean found = false; + for (ConsumesRequestCondition.ConsumeRequestCondition requestCondition : conditions) { + String conditionMediaType = requestCondition.getMediaType().toString(); + if (conditionMediaType.equals(s)) { + found = true; + break; + + } + } + if (!found) { + fail("Condition [" + s + "] not found"); + } + } + + + + } + + + + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java new file mode 100644 index 00000000000..d480015f19f --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java @@ -0,0 +1,150 @@ +/* + * Copyright 2002-2011 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 + * + * http://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.web.servlet.mvc.method.condition; + +import java.util.Set; + +import org.junit.Test; + +import org.springframework.mock.web.MockHttpServletRequest; + +import static org.junit.Assert.*; + +/** + * @author Arjen Poutsma + */ +public class HeadersRequestConditionTests { + + @Test + public void headerEquals() { + assertEquals(new HeadersRequestCondition("foo"), new HeadersRequestCondition("foo")); + assertEquals(new HeadersRequestCondition("foo"), new HeadersRequestCondition("FOO")); + assertFalse(new HeadersRequestCondition("foo").equals(new HeadersRequestCondition("bar"))); + assertEquals(new HeadersRequestCondition("foo=bar"), new HeadersRequestCondition("foo=bar")); + assertEquals(new HeadersRequestCondition("foo=bar"), new HeadersRequestCondition("FOO=bar")); + } + + @Test + public void headerPresent() { + RequestCondition condition = new HeadersRequestCondition("accept"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Accept", ""); + + assertTrue(condition.match(request)); + } + + @Test + public void headerPresentNoMatch() { + RequestCondition condition = new HeadersRequestCondition("foo"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("bar", ""); + + assertFalse(condition.match(request)); + } + + @Test + public void headerNotPresent() { + RequestCondition condition = new HeadersRequestCondition("!accept"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + + assertTrue(condition.match(request)); + } + + @Test + public void headerValueMatch() { + RequestCondition condition = new HeadersRequestCondition("foo=bar"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("foo", "bar"); + + assertTrue(condition.match(request)); + } + + @Test + public void headerValueNoMatch() { + RequestCondition condition = new HeadersRequestCondition("foo=bar"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("foo", "bazz"); + + assertFalse(condition.match(request)); + } + + @Test + public void headerCaseSensitiveValueMatch() { + RequestCondition condition = new HeadersRequestCondition("foo=Bar"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("foo", "bar"); + + assertFalse(condition.match(request)); + } + + @Test + public void headerValueMatchNegated() { + RequestCondition condition = new HeadersRequestCondition("foo!=bar"); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("foo", "baz"); + + assertTrue(condition.match(request)); + } + + @Test + public void compareTo() { + HeadersRequestCondition condition1 = new HeadersRequestCondition("foo", "bar", "baz"); + HeadersRequestCondition condition2 = new HeadersRequestCondition("foo", "bar"); + + int result = condition1.compareTo(condition2); + assertTrue("Invalid comparison result: " + result, result < 0); + + result = condition2.compareTo(condition1); + assertTrue("Invalid comparison result: " + result, result > 0); + } + + + @Test + public void combine() { + HeadersRequestCondition condition1 = new HeadersRequestCondition("foo=bar"); + HeadersRequestCondition condition2 = new HeadersRequestCondition("foo=baz"); + + HeadersRequestCondition result = condition1.combine(condition2); + Set conditions = result.getConditions(); + assertEquals(2, conditions.size()); + } + + @Test + public void getMatchingCondition() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("foo", "bar"); + + HeadersRequestCondition condition = new HeadersRequestCondition("foo"); + + HeadersRequestCondition result = condition.getMatchingCondition(request); + assertEquals(condition, result); + + condition = new HeadersRequestCondition("bar"); + + result = condition.getMatchingCondition(request); + assertNull(result); + } + + + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java new file mode 100644 index 00000000000..9ab48d15418 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestConditionTests.java @@ -0,0 +1,129 @@ +/* + * Copyright 2002-2011 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 + * + * http://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.web.servlet.mvc.method.condition; + +import java.util.Set; + +import org.junit.Test; + +import org.springframework.mock.web.MockHttpServletRequest; + +import static org.junit.Assert.*; + +/** + * @author Arjen Poutsma + */ +public class ParamsRequestConditionTests { + + @Test + public void paramEquals() { + assertEquals(new ParamsRequestCondition("foo"), new ParamsRequestCondition("foo")); + assertFalse(new ParamsRequestCondition("foo").equals(new ParamsRequestCondition("bar"))); + assertFalse(new ParamsRequestCondition("foo").equals(new ParamsRequestCondition("FOO"))); + assertEquals(new ParamsRequestCondition("foo=bar"), new ParamsRequestCondition("foo=bar")); + assertFalse( + new ParamsRequestCondition("foo=bar").equals(new ParamsRequestCondition("FOO=bar"))); + } + + @Test + public void paramPresent() { + RequestCondition condition = new ParamsRequestCondition("foo"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("foo", ""); + + assertTrue(condition.match(request)); + } + + @Test + public void paramPresentNoMatch() { + RequestCondition condition = new ParamsRequestCondition("foo"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("bar", ""); + + assertFalse(condition.match(request)); + } + + @Test + public void paramNotPresent() { + RequestCondition condition = new ParamsRequestCondition("!foo"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + + assertTrue(condition.match(request)); + } + + @Test + public void paramValueMatch() { + RequestCondition condition = new ParamsRequestCondition("foo=bar"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("foo", "bar"); + + assertTrue(condition.match(request)); + } + + @Test + public void paramValueNoMatch() { + RequestCondition condition = new ParamsRequestCondition("foo=bar"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("foo", "bazz"); + + assertFalse(condition.match(request)); + } + + @Test + public void compareTo() { + ParamsRequestCondition condition1 = new ParamsRequestCondition("foo", "bar", "baz"); + ParamsRequestCondition condition2 = new ParamsRequestCondition("foo", "bar"); + + int result = condition1.compareTo(condition2); + assertTrue("Invalid comparison result: " + result, result < 0); + + result = condition2.compareTo(condition1); + assertTrue("Invalid comparison result: " + result, result > 0); + } + + @Test + public void combine() { + ParamsRequestCondition condition1 = new ParamsRequestCondition("foo=bar"); + ParamsRequestCondition condition2 = new ParamsRequestCondition("foo=baz"); + + ParamsRequestCondition result = condition1.combine(condition2); + Set conditions = result.getConditions(); + assertEquals(2, conditions.size()); + } + + @Test + public void getMatchingCondition() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("foo", "bar"); + + ParamsRequestCondition condition = new ParamsRequestCondition("foo"); + + ParamsRequestCondition result = condition.getMatchingCondition(request); + assertEquals(condition, result); + + condition = new ParamsRequestCondition("bar"); + + result = condition.getMatchingCondition(request); + assertNull(result); + } + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactoryTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactoryTests.java deleted file mode 100644 index 4930c56a7b0..00000000000 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactoryTests.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2002-2011 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 - * - * http://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.web.servlet.mvc.method.condition; - -import org.junit.Test; - -import org.springframework.mock.web.MockHttpServletRequest; - -import static org.junit.Assert.*; - -/** - * @author Arjen Poutsma - */ -public class RequestConditionFactoryTests { - - - @Test - public void andMatch() { - RequestCondition condition1 = RequestConditionFactory.trueCondition(); - RequestCondition condition2 = RequestConditionFactory.trueCondition(); - RequestCondition and = RequestConditionFactory.and(condition1, condition2); - assertTrue(and.match(new MockHttpServletRequest())); - } - - @Test - public void andNoMatch() { - RequestCondition condition1 = RequestConditionFactory.trueCondition(); - RequestCondition condition2 = RequestConditionFactory.falseCondition(); - RequestCondition and = RequestConditionFactory.and(condition1, condition2); - assertFalse(and.match(new MockHttpServletRequest())); - } - - @Test - public void orMatch() { - RequestCondition condition1 = RequestConditionFactory.trueCondition(); - RequestCondition condition2 = RequestConditionFactory.falseCondition(); - RequestCondition and = RequestConditionFactory.or(condition1, condition2); - assertTrue(and.match(new MockHttpServletRequest())); - } - - @Test - public void orNoMatch() { - RequestCondition condition1 = RequestConditionFactory.falseCondition(); - RequestCondition condition2 = RequestConditionFactory.falseCondition(); - RequestCondition and = RequestConditionFactory.and(condition1, condition2); - assertFalse(and.match(new MockHttpServletRequest())); - } - - @Test - public void paramEquals() { - assertEquals(RequestConditionFactory.parseParams("foo"), RequestConditionFactory.parseParams("foo")); - assertFalse(RequestConditionFactory.parseParams("foo").equals(RequestConditionFactory.parseParams("bar"))); - assertFalse(RequestConditionFactory.parseParams("foo").equals(RequestConditionFactory.parseParams("FOO"))); - assertEquals(RequestConditionFactory.parseParams("foo=bar"), RequestConditionFactory.parseParams("foo=bar")); - assertFalse( - RequestConditionFactory.parseParams("foo=bar").equals(RequestConditionFactory.parseParams("FOO=bar"))); - } - - @Test - public void headerEquals() { - assertEquals(RequestConditionFactory.parseHeaders("foo"), RequestConditionFactory.parseHeaders("foo")); - assertEquals(RequestConditionFactory.parseHeaders("foo"), RequestConditionFactory.parseHeaders("FOO")); - assertFalse(RequestConditionFactory.parseHeaders("foo").equals(RequestConditionFactory.parseHeaders("bar"))); - assertEquals(RequestConditionFactory.parseHeaders("foo=bar"), RequestConditionFactory.parseHeaders("foo=bar")); - assertEquals(RequestConditionFactory.parseHeaders("foo=bar"), RequestConditionFactory.parseHeaders("FOO=bar")); - assertEquals(RequestConditionFactory.parseHeaders("content-type=text/xml"), - RequestConditionFactory.parseHeaders("Content-Type=TEXT/XML")); - } - - @Test - public void headerPresent() { - RequestCondition condition = RequestConditionFactory.parseHeaders("accept"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Accept", ""); - - assertTrue(condition.match(request)); - } - - @Test - public void headerPresentNoMatch() { - RequestCondition condition = RequestConditionFactory.parseHeaders("foo"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("bar", ""); - - assertFalse(condition.match(request)); - } - - @Test - public void headerNotPresent() { - RequestCondition condition = RequestConditionFactory.parseHeaders("!accept"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - - assertTrue(condition.match(request)); - } - - @Test - public void headerValueMatch() { - RequestCondition condition = RequestConditionFactory.parseHeaders("foo=bar"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("foo", "bar"); - - assertTrue(condition.match(request)); - } - - @Test - public void headerValueNoMatch() { - RequestCondition condition = RequestConditionFactory.parseHeaders("foo=bar"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("foo", "bazz"); - - assertFalse(condition.match(request)); - } - - @Test - public void headerCaseSensitiveValueMatch() { - RequestCondition condition = RequestConditionFactory.parseHeaders("foo=Bar"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("foo", "bar"); - - assertFalse(condition.match(request)); - } - - @Test - public void headerValueMatchNegated() { - RequestCondition condition = RequestConditionFactory.parseHeaders("foo!=bar"); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("foo", "baz"); - - assertTrue(condition.match(request)); - } - - @Test - public void mediaTypeHeaderValueMatch() { - RequestCondition condition = RequestConditionFactory.parseHeaders("accept=text/html"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Accept", "text/html"); - - assertTrue(condition.match(request)); - } - - @Test - public void mediaTypeHeaderValueMatchNegated() { - RequestCondition condition = RequestConditionFactory.parseHeaders("accept!=text/html"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Accept", "application/html"); - - assertTrue(condition.match(request)); - } - - @Test - public void consumesMatch() { - RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setContentType("text/plain"); - - assertTrue(condition.match(request)); - } - - @Test - public void consumesWildcardMatch() { - RequestCondition condition = RequestConditionFactory.parseConsumes("text/*"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setContentType("text/plain"); - - assertTrue(condition.match(request)); - } - - @Test - public void consumesMultipleMatch() { - RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain", "application/xml"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setContentType("text/plain"); - - assertTrue(condition.match(request)); - } - - @Test - public void consumesSingleNoMatch() { - RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain"); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setContentType("application/xml"); - - assertFalse(condition.match(request)); - } - -} 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 4961759d90f..67221d683fe 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 @@ -153,7 +153,7 @@ import java.lang.annotation.Target; * converters}. *
  • A {@link org.springframework.http.HttpEntity HttpEntity<?>} or * {@link org.springframework.http.ResponseEntity ResponseEntity<?>} object - * to access to the Servlet reponse HTTP headers and contents. The entity body will + * to access to the Servlet response HTTP headers and contents. The entity body will * be converted to the response stream using * {@linkplain org.springframework.http.converter.HttpMessageConverter message * converters}. @@ -297,4 +297,18 @@ public @interface RequestMapping { */ String[] headers() default {}; + /** + * The consumable media types of the mapped request, narrowing the primary mapping. + *

    The format is a sequence of media types ("text/plain", "application/*), + * with a request only mapped if the {@code Content-Type} matches one of these media types. + * Expressions can be negated by using the "!" operator, as in "!text/plain", which matches + * all requests with a {@code Content-Type} other than "text/plain". + *

    Supported at the type level as well as at the method level! + * When used at the type level, all method-level mappings override + * this consumes restriction. + * @see org.springframework.http.MediaType + * @see javax.servlet.http.HttpServletRequest#getContentType() + */ + String[] consumes() default "*/*"; + }