提交 71aae405 编写于 作者: A Arjen Poutsma

SPR-7354 - Added equivalent of JAX-RS @Consumes to Spring MVC

上级 7f247a6b
......@@ -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<R
return new RequestMappingInfo(Arrays.asList(annotation.value()), Arrays.asList(annotation.method()),
RequestConditionFactory.parseParams(annotation.params()),
RequestConditionFactory.parseHeaders(annotation.headers()),
RequestConditionFactory.parseConsumes());
RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()));
}
@Override
......@@ -240,6 +239,10 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
if (result != 0) {
return result;
}
result = mapping.getConsumes().compareTo(otherMapping.getConsumes());
if (result != 0) {
return result;
}
/*
TODO: fix
result = compareAcceptHeaders(mapping.getAcceptHeaderMediaTypes(), otherMapping.getAcceptHeaderMediaTypes());
......
......@@ -22,14 +22,14 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
/**
* Contains a set of conditions to match to a given request such as URL patterns, HTTP methods, request parameters
......@@ -49,16 +49,16 @@ public final class RequestMappingInfo {
private final Set<RequestMethod> 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.
*
* <p>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<String> patterns,
Collection<RequestMethod> 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<String> prependLeadingSlash(Collection<String> 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:
* <ul>
* <li>URL patterns:
* <ul>
......@@ -141,9 +148,9 @@ public final class RequestMappingInfo {
* <li>If neither contains patterns use ""
* </ul>
* <li>HTTP methods are combined as union of all HTTP methods listed in both keys.
* <li>Request parameter are combined into a logical AND.
* <li>Request header are combined into a logical AND.
* <li>Consumes .. TODO
* <li>Request parameters are combined as per {@link ParamsRequestCondition#combine(ParamsRequestCondition)}.
* <li>Request headers are combined as per {@link HeadersRequestCondition#combine(HeadersRequestCondition)}.
* <li>Consumes are combined as per {@link ConsumesRequestCondition#combine(ConsumesRequestCondition)}.
* </ul>
* @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<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
Set<RequestMethod> 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.
* <ul>
* <li>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<String> matchingPatterns = getMatchingPatterns(lookupPath, request, pathMatcher);
if (!matchingPatterns.isEmpty()) {
Set<RequestMethod> 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;
......
......@@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest;
* @author Arjen Poutsma
* @since 3.1
*/
abstract class AbstractNameValueCondition<T> extends AbstractRequestCondition {
abstract class AbstractNameValueCondition<T> implements RequestCondition {
protected final String name;
......@@ -65,11 +65,6 @@ abstract class AbstractNameValueCondition<T> extends AbstractRequestCondition {
protected abstract boolean matchValue(HttpServletRequest request);
@Override
public int getSpecificity() {
return 1;
}
@Override
public int hashCode() {
int result = name.hashCode();
......
/*
* 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();
}
}
......@@ -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<ConsumesRequestCondition.ConsumeRequestCondition>
implements Comparable<ConsumesRequestCondition> {
private final MediaType mediaType;
private final ConsumeRequestCondition mostSpecificCondition;
ConsumesRequestCondition(String mediaType) {
this.mediaType = MediaType.parseMediaType(mediaType);
ConsumesRequestCondition(Collection<ConsumeRequestCondition> 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<ConsumeRequestCondition> conditions = new ArrayList<ConsumeRequestCondition>(getConditions());
Collections.sort(conditions);
return conditions.get(0);
}
ConsumesRequestCondition(String... consumes) {
this(parseConditions(Arrays.asList(consumes)));
}
private static Set<ConsumeRequestCondition> parseConditions(List<String> consumes) {
if (consumes.isEmpty()) {
consumes = Collections.singletonList("*/*");
}
Set<ConsumeRequestCondition> conditions = new LinkedHashSet<ConsumeRequestCondition>(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<ConsumeRequestCondition> matchingConditions = new LinkedHashSet<ConsumeRequestCondition>(getConditions());
for (Iterator<ConsumeRequestCondition> iterator = matchingConditions.iterator(); iterator.hasNext();) {
ConsumeRequestCondition condition = iterator.next();
if (!condition.match(request)) {
iterator.remove();
}
}
if (matchingConditions.isEmpty()) {
return null;
}
else {
return new ConsumesRequestCondition(matchingConditions);
}
}
/**
* Combines this collection of request condition with another. Returns {@code other}, unless it has the default
* value (i.e. <code>&#42;/&#42;</code>).
*
* @param other the condition to combine with
*/
public ConsumesRequestCondition combine(ConsumesRequestCondition other) {
return !other.hasDefaultValue() ? other : this;
}
private boolean hasDefaultValue() {
Set<ConsumeRequestCondition> conditions = getConditions();
if (conditions.size() == 1) {
ConsumeRequestCondition condition = conditions.iterator().next();
return condition.getMediaType().equals(MediaType.ALL);
}
else {
contentType = MediaType.APPLICATION_OCTET_STREAM;
return false;
}
return this.mediaType.includes(contentType);
}
@Override
public int getSpecificity() {
return 1;
public int compareTo(ConsumesRequestCondition other) {
return this.mostSpecificCondition.compareTo(other.mostSpecificCondition);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
private static MediaType getContentType(HttpServletRequest request) {
if (StringUtils.hasLength(request.getContentType())) {
return MediaType.parseMediaType(request.getContentType());
}
if (obj != null && obj instanceof ConsumesRequestCondition) {
ConsumesRequestCondition other = (ConsumesRequestCondition) obj;
return this.mediaType.equals(other.mediaType);
else {
return MediaType.APPLICATION_OCTET_STREAM;
}
return false;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (mediaType != null) {
static class ConsumeRequestCondition implements RequestCondition, Comparable<ConsumeRequestCondition> {
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
}
/*
* 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<String> {
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;
}
}
/*
* 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<HeadersRequestCondition.HeaderRequestCondition>
implements Comparable<HeadersRequestCondition> {
HeadersRequestCondition(Collection<HeaderRequestCondition> conditions) {
super(conditions);
}
HeadersRequestCondition(String... headers) {
this(parseConditions(Arrays.asList(headers)));
}
private static Set<HeaderRequestCondition> parseConditions(Collection<String> params) {
Set<HeaderRequestCondition> conditions = new LinkedHashSet<HeaderRequestCondition>(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.<HeaderRequestCondition>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<HeaderRequestCondition> conditions = new LinkedHashSet<HeaderRequestCondition>(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<String> {
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;
}
}
}
......@@ -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<T extends RequestCondition> extends RequestConditionComposite<T> {
LogicalConjunctionRequestCondition(List<RequestCondition> conditions) {
LogicalConjunctionRequestCondition(Collection<T> conditions) {
super(conditions);
}
public boolean match(HttpServletRequest request) {
for (RequestCondition condition : conditions) {
Set<T> 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 " && ";
......
......@@ -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<T extends RequestCondition> extends RequestConditionComposite<T> {
LogicalDisjunctionRequestCondition(List<RequestCondition> conditions) {
LogicalDisjunctionRequestCondition(Collection<T> conditions) {
super(conditions);
}
public boolean match(HttpServletRequest request) {
for (RequestCondition condition : conditions) {
for (RequestCondition condition : getConditions()) {
if (condition.match(request)) {
return true;
}
......
/*
* 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() + ")";
}
}
/*
* 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<List<MediaType>> {
public MediaTypeHeaderRequestCondition(String expression) {
super(expression);
}
@Override
protected List<MediaType> 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<MediaType> 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;
}
}
/*
* 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<String> {
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;
}
}
/*
* 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<ParamsRequestCondition.ParamRequestCondition>
implements Comparable<ParamsRequestCondition> {
private ParamsRequestCondition(Collection<ParamRequestCondition> conditions) {
super(conditions);
}
ParamsRequestCondition(String... params) {
this(parseConditions(Arrays.asList(params)));
}
private static Set<ParamRequestCondition> parseConditions(List<String> params) {
Set<ParamRequestCondition> conditions = new LinkedHashSet<ParamRequestCondition>(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.<ParamRequestCondition>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<ParamRequestCondition> conditions = new LinkedHashSet<ParamRequestCondition>(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<String> {
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;
}
}
}
......@@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletRequest;
* @see RequestConditionFactory
* @since 3.1
*/
public interface RequestCondition extends Comparable<RequestCondition> {
public interface RequestCondition {
/**
* Indicates whether this condition matches against the given servlet request.
......
......@@ -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<T extends RequestCondition> implements RequestCondition {
protected final List<RequestCondition> conditions;
private final Set<T> conditions;
protected RequestConditionComposite(List<RequestCondition> conditions) {
Assert.notEmpty(conditions, "'conditions' must not be empty");
this.conditions = Collections.unmodifiableList(conditions);
protected RequestConditionComposite(Collection<T> conditions) {
this.conditions = Collections.unmodifiableSet(new LinkedHashSet<T>(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<T> 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<RequestCondition> iterator = conditions.iterator(); iterator.hasNext();) {
for (Iterator<T> iterator = conditions.iterator(); iterator.hasNext();) {
RequestCondition condition = iterator.next();
builder.append(condition.toString());
if (iterator.hasNext()) {
......
......@@ -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;
}
@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<RequestCondition> filteredConditions = new ArrayList<RequestCondition>(Arrays.asList(conditions));
for (Iterator<RequestCondition> 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);
}
}
private static final String CONTENT_TYPE_HEADER = "Content-Type";
/**
* 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<RequestCondition> filteredConditions = new ArrayList<RequestCondition>(Arrays.asList(conditions));
for (Iterator<RequestCondition> 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;
public static HeadersRequestCondition parseHeaders(String... headers) {
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
// filter out Accept and Content-Type headers, they are dealt with by produces and consumes respectively
Set<HeadersRequestCondition.HeaderRequestCondition> filteredConditions =
new LinkedHashSet<HeadersRequestCondition.HeaderRequestCondition>(headersCondition.getConditions());
for (Iterator<HeadersRequestCondition.HeaderRequestCondition> 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 and(result);
}
private static boolean isMediaTypeHeader(String name) {
return "Accept".equalsIgnoreCase(name) || "Content-Type".equalsIgnoreCase(name);
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<ConsumesRequestCondition.ConsumeRequestCondition> 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<ConsumesRequestCondition.ConsumeRequestCondition> parseContentTypeHeaders(String[] headers) {
List<ConsumesRequestCondition.ConsumeRequestCondition> allConditions =
new ArrayList<ConsumesRequestCondition.ConsumeRequestCondition>();
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) {
if (CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerCondition.value);
for (MediaType mediaType : mediaTypes) {
allConditions.add(
new ConsumesRequestCondition.ConsumeRequestCondition(mediaType, headerCondition.isNegated));
}
}
}
return or(result);
return allConditions;
}
}
/*
* 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<ConsumesRequestCondition.ConsumeRequestCondition> 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");
}
}
}
}
......@@ -16,6 +16,8 @@
package org.springframework.web.servlet.mvc.method.condition;
import java.util.Set;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
......@@ -25,65 +27,20 @@ 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()));
}
public class HeadersRequestConditionTests {
@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"));
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 = RequestConditionFactory.parseHeaders("accept");
RequestCondition condition = new HeadersRequestCondition("accept");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "");
......@@ -93,7 +50,7 @@ public class RequestConditionFactoryTests {
@Test
public void headerPresentNoMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo");
RequestCondition condition = new HeadersRequestCondition("foo");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("bar", "");
......@@ -103,7 +60,7 @@ public class RequestConditionFactoryTests {
@Test
public void headerNotPresent() {
RequestCondition condition = RequestConditionFactory.parseHeaders("!accept");
RequestCondition condition = new HeadersRequestCondition("!accept");
MockHttpServletRequest request = new MockHttpServletRequest();
......@@ -112,7 +69,7 @@ public class RequestConditionFactoryTests {
@Test
public void headerValueMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo=bar");
RequestCondition condition = new HeadersRequestCondition("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
......@@ -122,7 +79,7 @@ public class RequestConditionFactoryTests {
@Test
public void headerValueNoMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo=bar");
RequestCondition condition = new HeadersRequestCondition("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bazz");
......@@ -132,7 +89,7 @@ public class RequestConditionFactoryTests {
@Test
public void headerCaseSensitiveValueMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo=Bar");
RequestCondition condition = new HeadersRequestCondition("foo=Bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
......@@ -142,7 +99,7 @@ public class RequestConditionFactoryTests {
@Test
public void headerValueMatchNegated() {
RequestCondition condition = RequestConditionFactory.parseHeaders("foo!=bar");
RequestCondition condition = new HeadersRequestCondition("foo!=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "baz");
......@@ -150,63 +107,44 @@ public class RequestConditionFactoryTests {
}
@Test
public void mediaTypeHeaderValueMatch() {
RequestCondition condition = RequestConditionFactory.parseHeaders("accept=text/html");
public void compareTo() {
HeadersRequestCondition condition1 = new HeadersRequestCondition("foo", "bar", "baz");
HeadersRequestCondition condition2 = new HeadersRequestCondition("foo", "bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "text/html");
int result = condition1.compareTo(condition2);
assertTrue("Invalid comparison result: " + result, result < 0);
assertTrue(condition.match(request));
result = condition2.compareTo(condition1);
assertTrue("Invalid comparison result: " + result, result > 0);
}
@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");
public void combine() {
HeadersRequestCondition condition1 = new HeadersRequestCondition("foo=bar");
HeadersRequestCondition condition2 = new HeadersRequestCondition("foo=baz");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
assertTrue(condition.match(request));
HeadersRequestCondition result = condition1.combine(condition2);
Set<HeadersRequestCondition.HeaderRequestCondition> conditions = result.getConditions();
assertEquals(2, conditions.size());
}
@Test
public void consumesWildcardMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/*");
public void getMatchingCondition() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
request.addHeader("foo", "bar");
assertTrue(condition.match(request));
}
HeadersRequestCondition condition = new HeadersRequestCondition("foo");
@Test
public void consumesMultipleMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain", "application/xml");
HeadersRequestCondition result = condition.getMatchingCondition(request);
assertEquals(condition, result);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
condition = new HeadersRequestCondition("bar");
assertTrue(condition.match(request));
result = condition.getMatchingCondition(request);
assertNull(result);
}
@Test
public void consumesSingleNoMatch() {
RequestCondition condition = RequestConditionFactory.parseConsumes("text/plain");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("application/xml");
assertFalse(condition.match(request));
}
}
/*
* 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<ParamsRequestCondition.ParamRequestCondition> 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);
}
}
......@@ -153,7 +153,7 @@ import java.lang.annotation.Target;
* converters}.
* <li>A {@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} or
* {@link org.springframework.http.ResponseEntity ResponseEntity&lt;?&gt;} 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.
* <p>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".
* <p><b>Supported at the type level as well as at the method level!</b>
* 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 "*/*";
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册