Make HTTP methods a RequestCondition

This commit is contained in:
Arjen Poutsma
2011-05-11 10:38:30 +00:00
parent 80f0eabbd9
commit 9b0c66dc6c
12 changed files with 344 additions and 86 deletions

View File

@@ -124,7 +124,8 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
}
private static RequestMappingInfo createFromRequestMapping(RequestMapping annotation) {
return new RequestMappingInfo(Arrays.asList(annotation.value()), Arrays.asList(annotation.method()),
return new RequestMappingInfo(Arrays.asList(annotation.value()),
RequestConditionFactory.parseMethods(annotation.method()),
RequestConditionFactory.parseParams(annotation.params()),
RequestConditionFactory.parseHeaders(annotation.headers()),
RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()));
@@ -171,7 +172,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
for (RequestMappingInfo info : requestMappingInfos) {
for (String pattern : info.getPatterns()) {
if (pathMatcher.match(pattern, lookupPath)) {
for (RequestMethod method : info.getMethods()) {
for (RequestMethod method : info.getMethods().getMethods()) {
allowedMethods.add(method.name());
}
}
@@ -250,7 +251,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
return result;
}
*/
result = otherMapping.getMethods().size() - mapping.getMethods().size();
result = mapping.getMethods().compareTo(otherMapping.getMethods());
if (result != 0) {
return result;
}

View File

@@ -30,6 +30,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
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;
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
/**
* Contains a set of conditions to match to a given request such as URL patterns, HTTP methods, request parameters
@@ -47,7 +49,7 @@ public final class RequestMappingInfo {
private final Set<String> patterns;
private final Set<RequestMethod> methods;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
@@ -62,20 +64,20 @@ public final class RequestMappingInfo {
*
* <p>Package protected for testing purposes.
*/
RequestMappingInfo(Collection<String> patterns, Collection<RequestMethod> methods) {
this(patterns, methods, null, null, null);
RequestMappingInfo(Collection<String> patterns, RequestMethod[] methods) {
this(patterns, RequestConditionFactory.parseMethods(methods), null, null, null);
}
/**
* Creates a new {@code RequestMappingInfo} instance with a full set of conditions.
*/
public RequestMappingInfo(Collection<String> patterns,
Collection<RequestMethod> methods,
RequestMethodsRequestCondition methodsCondition,
ParamsRequestCondition paramsCondition,
HeadersRequestCondition headersCondition,
ConsumesRequestCondition consumesCondition) {
this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns));
this.methods = asUnmodifiableSet(methods);
this.methodsCondition = methodsCondition != null ? methodsCondition : new RequestMethodsRequestCondition();
this.paramsCondition = paramsCondition != null ? paramsCondition : new ParamsRequestCondition();
this.headersCondition = headersCondition != null ? headersCondition : new HeadersRequestCondition();
this.consumesCondition = consumesCondition != null ? consumesCondition : new ConsumesRequestCondition();
@@ -111,10 +113,10 @@ public final class RequestMappingInfo {
}
/**
* Returns the request methods of this request key.
* Returns the request method conditions of this request key.
*/
public Set<RequestMethod> getMethods() {
return methods;
public RequestMethodsRequestCondition getMethods() {
return methodsCondition;
}
/**
@@ -158,7 +160,7 @@ 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);
RequestMethodsRequestCondition methods = this.methodsCondition.combine(methodKey.methodsCondition);
ParamsRequestCondition params = this.paramsCondition.combine(methodKey.paramsCondition);
HeadersRequestCondition headers = this.headersCondition.combine(methodKey.headersCondition);
ConsumesRequestCondition consumes = this.consumesCondition.combine(methodKey.consumesCondition);
@@ -189,12 +191,6 @@ public final class RequestMappingInfo {
return result;
}
private static <T> Set<T> union(Collection<T> s1, Collection<T> s2) {
Set<T> union = new LinkedHashSet<T>(s1);
union.addAll(s2);
return union;
}
/**
* Returns a new {@code RequestMappingInfo} that contains all conditions of this key that are relevant to the request.
* <ul>
@@ -210,19 +206,19 @@ 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) {
RequestMethodsRequestCondition matchingMethodCondition = methodsCondition.getMatchingCondition(request);
ParamsRequestCondition matchingParamsCondition = paramsCondition.getMatchingCondition(request);
HeadersRequestCondition matchingHeadersCondition = headersCondition.getMatchingCondition(request);
ConsumesRequestCondition matchingConsumesCondition = consumesCondition.getMatchingCondition(request);
if (!checkMethod(request) || matchingParamsCondition == null || matchingHeadersCondition == null ||
if (matchingMethodCondition == null || matchingParamsCondition == null || matchingHeadersCondition == null ||
matchingConsumesCondition == null) {
return null;
}
else {
List<String> matchingPatterns = getMatchingPatterns(lookupPath, request, pathMatcher);
List<String> matchingPatterns = getMatchingPatterns(lookupPath, pathMatcher);
if (!matchingPatterns.isEmpty()) {
Set<RequestMethod> matchingMethods = getMatchingMethod(request);
return new RequestMappingInfo(matchingPatterns, matchingMethods, matchingParamsCondition,
return new RequestMappingInfo(matchingPatterns, matchingMethodCondition, matchingParamsCondition,
matchingHeadersCondition, matchingConsumesCondition);
}
else {
@@ -231,9 +227,7 @@ public final class RequestMappingInfo {
}
}
private List<String> getMatchingPatterns(String lookupPath,
HttpServletRequest request,
PathMatcher pathMatcher) {
private List<String> getMatchingPatterns(String lookupPath, PathMatcher pathMatcher) {
List<String> matchingPatterns = new ArrayList<String>();
for (String pattern : this.patterns) {
@@ -248,19 +242,6 @@ public final class RequestMappingInfo {
return matchingPatterns;
}
private Set<RequestMethod> getMatchingMethod(HttpServletRequest request) {
if (this.methods.isEmpty()) {
return this.methods;
}
else {
return Collections.singleton(RequestMethod.valueOf(request.getMethod()));
}
}
private boolean checkMethod(HttpServletRequest request) {
return methods.isEmpty() || methods.contains(RequestMethod.valueOf(request.getMethod()));
}
private String getMatchingPattern(String pattern, String lookupPath, PathMatcher pathMatcher) {
if (pattern.equals(lookupPath)) {
return pattern;
@@ -286,7 +267,8 @@ public final class RequestMappingInfo {
}
if (obj != null && obj instanceof RequestMappingInfo) {
RequestMappingInfo other = (RequestMappingInfo) obj;
return (this.patterns.equals(other.patterns) && this.methods.equals(other.methods) &&
return (this.patterns.equals(other.patterns) &&
this.methodsCondition.equals(other.methodsCondition) &&
this.paramsCondition.equals(other.paramsCondition) &&
this.headersCondition.equals(other.headersCondition) &&
this.consumesCondition.equals(other.consumesCondition));
@@ -299,7 +281,7 @@ public final class RequestMappingInfo {
int result = hash;
if (result == 0) {
result = patterns.hashCode();
result = 31 * result + methods.hashCode();
result = 31 * result + methodsCondition.hashCode();
result = 31 * result + paramsCondition.hashCode();
result = 31 * result + headersCondition.hashCode();
result = 31 * result + consumesCondition.hashCode();
@@ -312,13 +294,10 @@ public final class RequestMappingInfo {
public String toString() {
StringBuilder builder = new StringBuilder("{");
builder.append(patterns);
if (!methods.isEmpty()) {
builder.append(',');
builder.append(methods);
}
builder.append(",params=").append(paramsCondition.toString());
builder.append(",headers=").append(headersCondition.toString());
builder.append(",consumes=").append(consumesCondition.toString());
builder.append(",methods=").append(methodsCondition);
builder.append(",params=").append(paramsCondition);
builder.append(",headers=").append(headersCondition);
builder.append(",consumes=").append(consumesCondition);
builder.append('}');
return builder.toString();
}

View File

@@ -59,7 +59,7 @@ public class HeadersRequestCondition
}
/**
* Returns a new {@code RequestCondition} that contains all conditions of this key that match the request.
* Returns a new {@code RequestCondition} that contains all conditions 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

View File

@@ -34,7 +34,7 @@ class LogicalConjunctionRequestCondition<T extends RequestCondition> extends Req
public boolean match(HttpServletRequest request) {
Set<T> conditions = getConditions();
if (conditions.isEmpty()) {
if (isEmpty()) {
return true;
}
for (T condition : conditions) {

View File

@@ -32,6 +32,9 @@ class LogicalDisjunctionRequestCondition<T extends RequestCondition> extends Req
}
public boolean match(HttpServletRequest request) {
if (isEmpty()) {
return true;
}
for (RequestCondition condition : getConditions()) {
if (condition.match(request)) {
return true;

View File

@@ -62,7 +62,7 @@ public class ParamsRequestCondition
}
/**
* Returns a new {@code RequestCondition} that contains all conditions of this key that match the request.
* Returns a new {@code RequestCondition} that contains all conditions 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

View File

@@ -40,6 +40,10 @@ abstract class RequestConditionComposite<T extends RequestCondition> implements
return conditions;
}
boolean isEmpty() {
return conditions.isEmpty();
}
@Override
public boolean equals(Object o) {
if (this == o) {

View File

@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Set;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Factory for {@link RequestCondition} objects.
@@ -38,14 +39,25 @@ public abstract class RequestConditionFactory {
private static final String ACCEPT_HEADER = "Accept";
/**
* Parses the given parameters, and returns them as a single request conditions.
* Parses the given request methods, and returns them as a single request condition.
*
* @param methods the methods
* @return the request condition
* @see org.springframework.web.bind.annotation.RequestMapping#method()
*/
public static RequestMethodsRequestCondition parseMethods(RequestMethod... methods) {
return methods != null ? new RequestMethodsRequestCondition(methods) : new RequestMethodsRequestCondition();
}
/**
* Parses the given parameters, and returns them as a single request condition.
*
* @param params the parameters
* @return the request condition
* @see org.springframework.web.bind.annotation.RequestMapping#params()
*/
public static ParamsRequestCondition parseParams(String... params) {
return new ParamsRequestCondition(params);
return params != null ? new ParamsRequestCondition(params) : new ParamsRequestCondition();
}
/**
@@ -56,6 +68,9 @@ public abstract class RequestConditionFactory {
* @see org.springframework.web.bind.annotation.RequestMapping#headers()
*/
public static HeadersRequestCondition parseHeaders(String... headers) {
if (headers == null) {
return new HeadersRequestCondition();
}
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
// filter out Accept and Content-Type headers, they are dealt with by produces and consumes respectively
@@ -107,8 +122,8 @@ public abstract class RequestConditionFactory {
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));
allConditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(mediaType,
headerCondition.isNegated));
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.bind.annotation.RequestMethod;
/**
* Represents a collection of {@link RequestMethod} conditions, typically obtained from {@link
* org.springframework.web.bind.annotation.RequestMapping#method() @RequestMapping.methods()}.
*
* @author Arjen Poutsma
* @see RequestConditionFactory#parseMethods(RequestMethod...)
* @since 3.1
*/
public class RequestMethodsRequestCondition
extends LogicalDisjunctionRequestCondition<RequestMethodsRequestCondition.RequestMethodRequestCondition>
implements Comparable<RequestMethodsRequestCondition> {
private RequestMethodsRequestCondition(Collection<RequestMethodRequestCondition> conditions) {
super(conditions);
}
RequestMethodsRequestCondition(RequestMethod... methods) {
this(parseConditions(Arrays.asList(methods)));
}
private static Set<RequestMethodRequestCondition> parseConditions(List<RequestMethod> methods) {
Set<RequestMethodRequestCondition> conditions =
new LinkedHashSet<RequestMethodRequestCondition>(methods.size());
for (RequestMethod method : methods) {
conditions.add(new RequestMethodRequestCondition(method));
}
return conditions;
}
/**
* Creates an empty set of method request conditions.
*/
public RequestMethodsRequestCondition() {
this(Collections.<RequestMethodRequestCondition>emptySet());
}
/**
* Returns all {@link RequestMethod}s contained in this condition.
*/
public Set<RequestMethod> getMethods() {
Set<RequestMethod> result = new LinkedHashSet<RequestMethod>();
for (RequestMethodRequestCondition condition : getConditions()) {
result.add(condition.getMethod());
}
return result;
}
public int compareTo(RequestMethodsRequestCondition other) {
return other.getConditions().size() - this.getConditions().size();
}
/**
* Returns a new {@code RequestMethodsRequestCondition} that contains all conditions 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 RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
if (isEmpty()) {
return this;
}
else {
if (match(request)) {
return new RequestMethodsRequestCondition(RequestMethod.valueOf(request.getMethod()));
}
else {
return null;
}
}
}
/**
* Combines this collection of request method conditions with another by combining all methods into a logical OR.
*
* @param other the condition to combine with
*/
public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) {
Set<RequestMethodRequestCondition> conditions =
new LinkedHashSet<RequestMethodRequestCondition>(getConditions());
conditions.addAll(other.getConditions());
return new RequestMethodsRequestCondition(conditions);
}
static class RequestMethodRequestCondition implements RequestCondition {
private final RequestMethod method;
RequestMethodRequestCondition(RequestMethod method) {
this.method = method;
}
RequestMethod getMethod() {
return method;
}
public boolean match(HttpServletRequest request) {
RequestMethod method = RequestMethod.valueOf(request.getMethod());
return this.method.equals(method);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof RequestMethodRequestCondition) {
RequestMethodRequestCondition other = (RequestMethodRequestCondition) obj;
return this.method.equals(other.method);
}
return false;
}
@Override
public int hashCode() {
return method.hashCode();
}
@Override
public String toString() {
return method.toString();
}
}
}