Add RequestCondition hierarchy
A pretty complete equivalent to the same in spring-webmvc except for CORS checks, and custom HTTP methods. Another notable difference is that the "params" condition works on query params strictly.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.HttpMediaTypeException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Supports media type expressions as described in:
|
||||
* {@link RequestMapping#consumes()} and {@link RequestMapping#produces()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
abstract class AbstractMediaTypeExpression implements Comparable<AbstractMediaTypeExpression>, MediaTypeExpression {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final MediaType mediaType;
|
||||
|
||||
private final boolean isNegated;
|
||||
|
||||
|
||||
AbstractMediaTypeExpression(String expression) {
|
||||
if (expression.startsWith("!")) {
|
||||
this.isNegated = true;
|
||||
expression = expression.substring(1);
|
||||
}
|
||||
else {
|
||||
this.isNegated = false;
|
||||
}
|
||||
this.mediaType = MediaType.parseMediaType(expression);
|
||||
}
|
||||
|
||||
AbstractMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
this.mediaType = mediaType;
|
||||
this.isNegated = negated;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MediaType getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNegated() {
|
||||
return this.isNegated;
|
||||
}
|
||||
|
||||
|
||||
public final boolean match(ServerWebExchange exchange) {
|
||||
try {
|
||||
boolean match = matchMediaType(exchange);
|
||||
return (!this.isNegated == match);
|
||||
}
|
||||
catch (HttpMediaTypeException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean matchMediaType(ServerWebExchange exchange) throws HttpMediaTypeException;
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(AbstractMediaTypeExpression other) {
|
||||
return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && getClass() == obj.getClass()) {
|
||||
AbstractMediaTypeExpression other = (AbstractMediaTypeExpression) obj;
|
||||
return (this.mediaType.equals(other.mediaType) && this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.mediaType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (this.isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(this.mediaType.toString());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Supports "name=value" style expressions as described in:
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping#params()} and
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping#headers()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final T value;
|
||||
|
||||
protected final boolean isNegated;
|
||||
|
||||
AbstractNameValueExpression(String expression) {
|
||||
int separator = expression.indexOf('=');
|
||||
if (separator == -1) {
|
||||
this.isNegated = expression.startsWith("!");
|
||||
this.name = isNegated ? expression.substring(1) : expression;
|
||||
this.value = null;
|
||||
}
|
||||
else {
|
||||
this.isNegated = (separator > 0) && (expression.charAt(separator - 1) == '!');
|
||||
this.name = isNegated ? expression.substring(0, separator - 1) : expression.substring(0, separator);
|
||||
this.value = parseValue(expression.substring(separator + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNegated() {
|
||||
return this.isNegated;
|
||||
}
|
||||
|
||||
protected abstract boolean isCaseSensitiveName();
|
||||
|
||||
protected abstract T parseValue(String valueExpression);
|
||||
|
||||
public final boolean match(ServerWebExchange exchange) {
|
||||
boolean isMatch;
|
||||
if (this.value != null) {
|
||||
isMatch = matchValue(exchange);
|
||||
}
|
||||
else {
|
||||
isMatch = matchName(exchange);
|
||||
}
|
||||
return this.isNegated != isMatch;
|
||||
}
|
||||
|
||||
protected abstract boolean matchName(ServerWebExchange exchange);
|
||||
|
||||
protected abstract boolean matchValue(ServerWebExchange exchange);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof AbstractNameValueExpression) {
|
||||
AbstractNameValueExpression<?> other = (AbstractNameValueExpression<?>) obj;
|
||||
String thisName = isCaseSensitiveName() ? this.name : this.name.toLowerCase();
|
||||
String otherName = isCaseSensitiveName() ? other.name : other.name.toLowerCase();
|
||||
return ((thisName.equalsIgnoreCase(otherName)) &&
|
||||
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
|
||||
this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = isCaseSensitiveName() ? name.hashCode() : name.toLowerCase().hashCode();
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
result = 31 * result + (isNegated ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (value != null) {
|
||||
builder.append(name);
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append('=');
|
||||
builder.append(value);
|
||||
}
|
||||
else {
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(name);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* A base class for {@link RequestCondition} types providing implementations of
|
||||
* {@link #equals(Object)}, {@link #hashCode()}, and {@link #toString()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>>
|
||||
implements RequestCondition<T> {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && getClass() == obj.getClass()) {
|
||||
AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) obj;
|
||||
return getContent().equals(other.getContent());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getContent().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("[");
|
||||
for (Iterator<?> iterator = getContent().iterator(); iterator.hasNext();) {
|
||||
Object expression = iterator.next();
|
||||
builder.append(expression.toString());
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(getToStringInfix());
|
||||
}
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this condition is empty, i.e. whether or not it
|
||||
* contains any discrete items.
|
||||
* @return {@code true} if empty; {@code false} otherwise
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return getContent().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the discrete items a request condition is composed of.
|
||||
* <p>For example URL patterns, HTTP request methods, param expressions, etc.
|
||||
* @return a collection of objects, never {@code null}
|
||||
*/
|
||||
protected abstract Collection<?> getContent();
|
||||
|
||||
/**
|
||||
* The notation to use when printing discrete items of content.
|
||||
* <p>For example {@code " || "} for URL patterns or {@code " && "}
|
||||
* for param expressions.
|
||||
*/
|
||||
protected abstract String getToStringInfix();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Implements the {@link RequestCondition} contract by delegating to multiple
|
||||
* {@code RequestCondition} types and using a logical conjunction (' && ') to
|
||||
* ensure all conditions match a given request.
|
||||
*
|
||||
* <p>When {@code CompositeRequestCondition} instances are combined or compared
|
||||
* they are expected to (a) contain the same number of conditions and (b) that
|
||||
* conditions in the respective index are of the same type. It is acceptable to
|
||||
* provide {@code null} conditions or no conditions at all to the constructor.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class CompositeRequestCondition extends AbstractRequestCondition<CompositeRequestCondition> {
|
||||
|
||||
private final RequestConditionHolder[] requestConditions;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance with 0 or more {@code RequestCondition} types. It is
|
||||
* important to create {@code CompositeRequestCondition} instances with the
|
||||
* same number of conditions so they may be compared and combined.
|
||||
* It is acceptable to provide {@code null} conditions.
|
||||
*/
|
||||
public CompositeRequestCondition(RequestCondition<?>... requestConditions) {
|
||||
this.requestConditions = wrap(requestConditions);
|
||||
}
|
||||
|
||||
private CompositeRequestCondition(RequestConditionHolder[] requestConditions) {
|
||||
this.requestConditions = requestConditions;
|
||||
}
|
||||
|
||||
|
||||
private RequestConditionHolder[] wrap(RequestCondition<?>... rawConditions) {
|
||||
RequestConditionHolder[] wrappedConditions = new RequestConditionHolder[rawConditions.length];
|
||||
for (int i = 0; i < rawConditions.length; i++) {
|
||||
wrappedConditions[i] = new RequestConditionHolder(rawConditions[i]);
|
||||
}
|
||||
return wrappedConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this instance contains 0 conditions or not.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return ObjectUtils.isEmpty(this.requestConditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying conditions, possibly empty but never {@code null}.
|
||||
*/
|
||||
public List<RequestCondition<?>> getConditions() {
|
||||
return unwrap();
|
||||
}
|
||||
|
||||
private List<RequestCondition<?>> unwrap() {
|
||||
List<RequestCondition<?>> result = new ArrayList<>();
|
||||
for (RequestConditionHolder holder : this.requestConditions) {
|
||||
result.add(holder.getCondition());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<?> getContent() {
|
||||
return (isEmpty()) ? Collections.emptyList() : getConditions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
|
||||
private int getLength() {
|
||||
return this.requestConditions.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* If one instance is empty, return the other.
|
||||
* If both instances have conditions, combine the individual conditions
|
||||
* after ensuring they are of the same type and number.
|
||||
*/
|
||||
@Override
|
||||
public CompositeRequestCondition combine(CompositeRequestCondition other) {
|
||||
if (isEmpty() && other.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
else if (other.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
else if (isEmpty()) {
|
||||
return other;
|
||||
}
|
||||
else {
|
||||
assertNumberOfConditions(other);
|
||||
RequestConditionHolder[] combinedConditions = new RequestConditionHolder[getLength()];
|
||||
for (int i = 0; i < getLength(); i++) {
|
||||
combinedConditions[i] = this.requestConditions[i].combine(other.requestConditions[i]);
|
||||
}
|
||||
return new CompositeRequestCondition(combinedConditions);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNumberOfConditions(CompositeRequestCondition other) {
|
||||
Assert.isTrue(getLength() == other.getLength(),
|
||||
"Cannot combine CompositeRequestConditions with a different number of conditions. " +
|
||||
ObjectUtils.nullSafeToString(this.requestConditions) + " and " +
|
||||
ObjectUtils.nullSafeToString(other.requestConditions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate to <em>all</em> contained conditions to match the request and return the
|
||||
* resulting "matching" condition instances.
|
||||
* <p>An empty {@code CompositeRequestCondition} matches to all requests.
|
||||
*/
|
||||
@Override
|
||||
public CompositeRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
if (isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
RequestConditionHolder[] matchingConditions = new RequestConditionHolder[getLength()];
|
||||
for (int i = 0; i < getLength(); i++) {
|
||||
matchingConditions[i] = this.requestConditions[i].getMatchingCondition(exchange);
|
||||
if (matchingConditions[i] == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new CompositeRequestCondition(matchingConditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* If one instance is empty, the other "wins". If both instances have
|
||||
* conditions, compare them in the order in which they were provided.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(CompositeRequestCondition other, ServerWebExchange exchange) {
|
||||
if (isEmpty() && other.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
else if (isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
else if (other.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
assertNumberOfConditions(other);
|
||||
for (int i = 0; i < getLength(); i++) {
|
||||
int result = this.requestConditions[i].compareTo(other.requestConditions[i], exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A logical disjunction (' || ') request condition to match a request's
|
||||
* 'Content-Type' header to a list of media type expressions. Two kinds of
|
||||
* media type expressions are supported, which are described in
|
||||
* {@link RequestMapping#consumes()} and {@link RequestMapping#headers()}
|
||||
* where the header name is 'Content-Type'. Regardless of which syntax is
|
||||
* used, the semantics are the same.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class ConsumesRequestCondition extends AbstractRequestCondition<ConsumesRequestCondition> {
|
||||
|
||||
// private final static ConsumesRequestCondition PRE_FLIGHT_MATCH = new ConsumesRequestCondition();
|
||||
|
||||
|
||||
private final List<ConsumeMediaTypeExpression> expressions;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance from 0 or more "consumes" expressions.
|
||||
* @param consumes expressions with the syntax described in
|
||||
* {@link RequestMapping#consumes()}; if 0 expressions are provided,
|
||||
* the condition will match to every request
|
||||
*/
|
||||
public ConsumesRequestCondition(String... consumes) {
|
||||
this(consumes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with "consumes" and "header" expressions.
|
||||
* "Header" expressions where the header name is not 'Content-Type' or have
|
||||
* no header value defined are ignored. If 0 expressions are provided in
|
||||
* total, the condition will match to every request
|
||||
* @param consumes as described in {@link RequestMapping#consumes()}
|
||||
* @param headers as described in {@link RequestMapping#headers()}
|
||||
*/
|
||||
public ConsumesRequestCondition(String[] consumes, String[] headers) {
|
||||
this(parseExpressions(consumes, headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor accepting parsed media type expressions.
|
||||
*/
|
||||
private ConsumesRequestCondition(Collection<ConsumeMediaTypeExpression> expressions) {
|
||||
this.expressions = new ArrayList<>(expressions);
|
||||
Collections.sort(this.expressions);
|
||||
}
|
||||
|
||||
|
||||
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) {
|
||||
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeadersRequestCondition.HeaderExpression expr = new HeadersRequestCondition.HeaderExpression(header);
|
||||
if ("Content-Type".equalsIgnoreCase(expr.name)) {
|
||||
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
|
||||
result.add(new ConsumeMediaTypeExpression(mediaType, expr.isNegated));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (consumes != null) {
|
||||
for (String consume : consumes) {
|
||||
result.add(new ConsumeMediaTypeExpression(consume));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the contained MediaType expressions.
|
||||
*/
|
||||
public Set<MediaTypeExpression> getExpressions() {
|
||||
return new LinkedHashSet<>(this.expressions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the media types for this condition excluding negated expressions.
|
||||
*/
|
||||
public Set<MediaType> getConsumableMediaTypes() {
|
||||
Set<MediaType> result = new LinkedHashSet<>();
|
||||
for (ConsumeMediaTypeExpression expression : this.expressions) {
|
||||
if (!expression.isNegated()) {
|
||||
result.add(expression.getMediaType());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the condition has any media type expressions.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.expressions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConsumeMediaTypeExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "other" instance if it has any expressions; returns "this"
|
||||
* instance otherwise. Practically that means a method-level "consumes"
|
||||
* overrides a type-level "consumes" condition.
|
||||
*/
|
||||
@Override
|
||||
public ConsumesRequestCondition combine(ConsumesRequestCondition other) {
|
||||
return !other.expressions.isEmpty() ? other : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any of the contained media type expressions match the given
|
||||
* request 'Content-Type' header and returns an instance that is guaranteed
|
||||
* to contain matching expressions only. The match is performed via
|
||||
* {@link MediaType#includes(MediaType)}.
|
||||
* @param exchange the current exchange
|
||||
* @return the same instance if the condition contains no expressions;
|
||||
* or a new condition with matching expressions only;
|
||||
* or {@code null} if no expressions match.
|
||||
*/
|
||||
@Override
|
||||
public ConsumesRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
// if (CorsUtils.isPreFlightRequest(request)) {
|
||||
// return PRE_FLIGHT_MATCH;
|
||||
// }
|
||||
if (isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>(expressions);
|
||||
for (Iterator<ConsumeMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
|
||||
ConsumeMediaTypeExpression expression = iterator.next();
|
||||
if (!expression.match(exchange)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return (result.isEmpty()) ? null : new ConsumesRequestCondition(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* <ul>
|
||||
* <li>0 if the two conditions have the same number of expressions
|
||||
* <li>Less than 0 if "this" has more or more specific media type expressions
|
||||
* <li>Greater than 0 if "other" has more or more specific media type expressions
|
||||
* </ul>
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} and each instance contains
|
||||
* the matching consumable media type expression only or is otherwise empty.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(ConsumesRequestCondition other, ServerWebExchange exchange) {
|
||||
if (this.expressions.isEmpty() && other.expressions.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
else if (this.expressions.isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
else if (other.expressions.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return this.expressions.get(0).compareTo(other.expressions.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses and matches a single media type expression to a request's 'Content-Type' header.
|
||||
*/
|
||||
static class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression {
|
||||
|
||||
ConsumeMediaTypeExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
super(mediaType, negated);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchMediaType(ServerWebExchange exchange) throws HttpMediaTypeNotSupportedException {
|
||||
try {
|
||||
MediaType contentType = exchange.getRequest().getHeaders().getContentType();
|
||||
contentType = (contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM);
|
||||
return getMediaType().includes(contentType);
|
||||
}
|
||||
catch (InvalidMediaTypeException ex) {
|
||||
throw new HttpMediaTypeNotSupportedException("Can't parse Content-Type [" +
|
||||
exchange.getRequest().getHeaders().getFirst("Content-Type") +
|
||||
"]: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A logical conjunction (' && ') request condition that matches a request against
|
||||
* a set of header expressions with syntax defined in {@link RequestMapping#headers()}.
|
||||
*
|
||||
* <p>Expressions passed to the constructor with header names 'Accept' or
|
||||
* 'Content-Type' are ignored. See {@link ConsumesRequestCondition} and
|
||||
* {@link ProducesRequestCondition} for those.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class HeadersRequestCondition extends AbstractRequestCondition<HeadersRequestCondition> {
|
||||
|
||||
// private final static HeadersRequestCondition PRE_FLIGHT_MATCH = new HeadersRequestCondition();
|
||||
|
||||
|
||||
private final Set<HeaderExpression> expressions;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance from the given header expressions. Expressions with
|
||||
* header names 'Accept' or 'Content-Type' are ignored. See {@link ConsumesRequestCondition}
|
||||
* and {@link ProducesRequestCondition} for those.
|
||||
* @param headers media type expressions with syntax defined in {@link RequestMapping#headers()};
|
||||
* if 0, the condition will match to every request
|
||||
*/
|
||||
public HeadersRequestCondition(String... headers) {
|
||||
this(parseExpressions(headers));
|
||||
}
|
||||
|
||||
private HeadersRequestCondition(Collection<HeaderExpression> conditions) {
|
||||
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions));
|
||||
}
|
||||
|
||||
|
||||
private static Collection<HeaderExpression> parseExpressions(String... headers) {
|
||||
Set<HeaderExpression> expressions = new LinkedHashSet<HeaderExpression>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeaderExpression expr = new HeaderExpression(header);
|
||||
if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
|
||||
continue;
|
||||
}
|
||||
expressions.add(expr);
|
||||
}
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contained request header expressions.
|
||||
*/
|
||||
public Set<NameValueExpression<String>> getExpressions() {
|
||||
return new LinkedHashSet<>(this.expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<HeaderExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance with the union of the header expressions
|
||||
* from "this" and the "other" instance.
|
||||
*/
|
||||
@Override
|
||||
public HeadersRequestCondition combine(HeadersRequestCondition other) {
|
||||
Set<HeaderExpression> set = new LinkedHashSet<>(this.expressions);
|
||||
set.addAll(other.expressions);
|
||||
return new HeadersRequestCondition(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "this" instance if the request matches all expressions;
|
||||
* or {@code null} otherwise.
|
||||
*/
|
||||
@Override
|
||||
public HeadersRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
// if (CorsUtils.isPreFlightRequest(request)) {
|
||||
// return PRE_FLIGHT_MATCH;
|
||||
// }
|
||||
for (HeaderExpression expression : expressions) {
|
||||
if (!expression.match(exchange)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* <ul>
|
||||
* <li>0 if the two conditions have the same number of header expressions
|
||||
* <li>Less than 0 if "this" instance has more header expressions
|
||||
* <li>Greater than 0 if the "other" instance has more header expressions
|
||||
* </ul>
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} and each instance
|
||||
* contains the matching header expression only or is otherwise empty.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(HeadersRequestCondition other, ServerWebExchange exchange) {
|
||||
return other.expressions.size() - this.expressions.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses and matches a single header expression to a request.
|
||||
*/
|
||||
static class HeaderExpression extends AbstractNameValueExpression<String> {
|
||||
|
||||
public HeaderExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseSensitiveName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(ServerWebExchange exchange) {
|
||||
return exchange.getRequest().getHeaders().get(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(ServerWebExchange exchange) {
|
||||
return value.equals(exchange.getRequest().getHeaders().getFirst(name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* A contract for media type expressions (e.g. "text/plain", "!text/plain") as
|
||||
* defined in the {@code @RequestMapping} annotation for "consumes" and
|
||||
* "produces" conditions.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public interface MediaTypeExpression {
|
||||
|
||||
MediaType getMediaType();
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* A contract for {@code "name!=value"} style expression used to specify request
|
||||
* parameters and request header conditions in {@code @RequestMapping}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public interface NameValueExpression<T> {
|
||||
|
||||
String getName();
|
||||
|
||||
T getValue();
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A logical conjunction (' && ') request condition that matches a request against
|
||||
* a set parameter expressions with syntax defined in {@link RequestMapping#params()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class ParamsRequestCondition extends AbstractRequestCondition<ParamsRequestCondition> {
|
||||
|
||||
private final Set<ParamExpression> expressions;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance from the given param expressions.
|
||||
* @param params expressions with syntax defined in {@link RequestMapping#params()};
|
||||
* if 0, the condition will match to every request.
|
||||
*/
|
||||
public ParamsRequestCondition(String... params) {
|
||||
this(parseExpressions(params));
|
||||
}
|
||||
|
||||
private ParamsRequestCondition(Collection<ParamExpression> conditions) {
|
||||
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions));
|
||||
}
|
||||
|
||||
|
||||
private static Collection<ParamExpression> parseExpressions(String... params) {
|
||||
Set<ParamExpression> expressions = new LinkedHashSet<>();
|
||||
if (params != null) {
|
||||
for (String param : params) {
|
||||
expressions.add(new ParamExpression(param));
|
||||
}
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the contained request parameter expressions.
|
||||
*/
|
||||
public Set<NameValueExpression<String>> getExpressions() {
|
||||
return new LinkedHashSet<>(this.expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ParamExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance with the union of the param expressions
|
||||
* from "this" and the "other" instance.
|
||||
*/
|
||||
@Override
|
||||
public ParamsRequestCondition combine(ParamsRequestCondition other) {
|
||||
Set<ParamExpression> set = new LinkedHashSet<>(this.expressions);
|
||||
set.addAll(other.expressions);
|
||||
return new ParamsRequestCondition(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "this" instance if the request matches all param expressions;
|
||||
* or {@code null} otherwise.
|
||||
*/
|
||||
@Override
|
||||
public ParamsRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
for (ParamExpression expression : expressions) {
|
||||
if (!expression.match(exchange)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* <ul>
|
||||
* <li>0 if the two conditions have the same number of parameter expressions
|
||||
* <li>Less than 0 if "this" instance has more parameter expressions
|
||||
* <li>Greater than 0 if the "other" instance has more parameter expressions
|
||||
* </ul>
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} and each instance
|
||||
* contains the matching parameter expressions only or is otherwise empty.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(ParamsRequestCondition other, ServerWebExchange exchange) {
|
||||
return (other.expressions.size() - this.expressions.size());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses and matches a single param expression to a request.
|
||||
*/
|
||||
static class ParamExpression extends AbstractNameValueExpression<String> {
|
||||
|
||||
ParamExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseSensitiveName() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(ServerWebExchange exchange) {
|
||||
return exchange.getRequest().getQueryParams().containsKey(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(ServerWebExchange exchange) {
|
||||
return this.value.equals(exchange.getRequest().getQueryParams().getFirst(this.name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.HttpRequestPathHelper;
|
||||
|
||||
/**
|
||||
* A logical disjunction (' || ') request condition that matches a request
|
||||
* against a set of URL path patterns.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
|
||||
|
||||
private final Set<String> patterns;
|
||||
|
||||
private final HttpRequestPathHelper pathHelper;
|
||||
|
||||
private final PathMatcher pathMatcher;
|
||||
|
||||
private final boolean useSuffixPatternMatch;
|
||||
|
||||
private final boolean useTrailingSlashMatch;
|
||||
|
||||
private final List<String> fileExtensions = new ArrayList<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given URL patterns.
|
||||
* Each pattern that is not empty and does not start with "/" is prepended with "/".
|
||||
* @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
|
||||
*/
|
||||
public PatternsRequestCondition(String... patterns) {
|
||||
this(asList(patterns), null, null, true, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given URL patterns.
|
||||
* Each pattern that is not empty and does not start with "/" is pre-pended with "/".
|
||||
* @param patterns the URL patterns to use; if 0, the condition will match to every request.
|
||||
* @param pathHelper to determine the lookup path for a request
|
||||
* @param pathMatcher for pattern path matching
|
||||
* @param useSuffixPatternMatch whether to enable matching by suffix (".*")
|
||||
* @param useTrailingSlashMatch whether to match irrespective of a trailing slash
|
||||
* @param extensions file extensions to consider for path matching
|
||||
*/
|
||||
public PatternsRequestCondition(String[] patterns, HttpRequestPathHelper pathHelper,
|
||||
PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
|
||||
List<String> extensions) {
|
||||
|
||||
this(asList(patterns), pathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor accepting a collection of patterns.
|
||||
*/
|
||||
private PatternsRequestCondition(Collection<String> patterns, HttpRequestPathHelper pathHelper,
|
||||
PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
|
||||
List<String> fileExtensions) {
|
||||
|
||||
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
|
||||
this.pathHelper = (pathHelper != null ? pathHelper : new HttpRequestPathHelper());
|
||||
this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher());
|
||||
this.useSuffixPatternMatch = useSuffixPatternMatch;
|
||||
this.useTrailingSlashMatch = useTrailingSlashMatch;
|
||||
if (fileExtensions != null) {
|
||||
for (String fileExtension : fileExtensions) {
|
||||
if (fileExtension.charAt(0) != '.') {
|
||||
fileExtension = "." + fileExtension;
|
||||
}
|
||||
this.fileExtensions.add(fileExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static List<String> asList(String... patterns) {
|
||||
return (patterns != null ? Arrays.asList(patterns) : Collections.emptyList());
|
||||
}
|
||||
|
||||
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
|
||||
if (patterns == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> result = new LinkedHashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
|
||||
pattern = "/" + pattern;
|
||||
}
|
||||
result.add(pattern);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<String> getPatterns() {
|
||||
return this.patterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getContent() {
|
||||
return this.patterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance with URL patterns from the current instance ("this") and
|
||||
* the "other" instance as follows:
|
||||
* <ul>
|
||||
* <li>If there are patterns in both instances, combine the patterns in "this" with
|
||||
* the patterns in "other" using {@link PathMatcher#combine(String, String)}.
|
||||
* <li>If only one instance has patterns, use them.
|
||||
* <li>If neither instance has patterns, use an empty String (i.e. "").
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public PatternsRequestCondition combine(PatternsRequestCondition other) {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
|
||||
for (String pattern1 : this.patterns) {
|
||||
for (String pattern2 : other.patterns) {
|
||||
result.add(this.pathMatcher.combine(pattern1, pattern2));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!this.patterns.isEmpty()) {
|
||||
result.addAll(this.patterns);
|
||||
}
|
||||
else if (!other.patterns.isEmpty()) {
|
||||
result.addAll(other.patterns);
|
||||
}
|
||||
else {
|
||||
result.add("");
|
||||
}
|
||||
return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
this.useTrailingSlashMatch, this.fileExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any of the patterns match the given request and returns an instance
|
||||
* that is guaranteed to contain matching patterns, sorted via
|
||||
* {@link PathMatcher#getPatternComparator(String)}.
|
||||
* <p>A matching pattern is obtained by making checks in the following order:
|
||||
* <ul>
|
||||
* <li>Direct match
|
||||
* <li>Pattern match with ".*" appended if the pattern doesn't already contain a "."
|
||||
* <li>Pattern match
|
||||
* <li>Pattern match with "/" appended if the pattern doesn't already end in "/"
|
||||
* </ul>
|
||||
* @param exchange the current exchange
|
||||
* @return the same instance if the condition contains no patterns;
|
||||
* or a new condition with sorted matching patterns;
|
||||
* or {@code null} if no patterns match.
|
||||
*/
|
||||
@Override
|
||||
public PatternsRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
if (this.patterns.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
|
||||
List<String> matches = getMatchingPatterns(lookupPath);
|
||||
|
||||
return matches.isEmpty() ? null :
|
||||
new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
this.useTrailingSlashMatch, this.fileExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the patterns matching the given lookup path. Invoking this method should
|
||||
* yield results equivalent to those of calling
|
||||
* {@link #getMatchingCondition(ServerWebExchange)}.
|
||||
* This method is provided as an alternative to be used if no request is available
|
||||
* (e.g. introspection, tooling, etc).
|
||||
* @param lookupPath the lookup path to match to existing patterns
|
||||
* @return a collection of matching patterns sorted with the closest match at the top
|
||||
*/
|
||||
public List<String> getMatchingPatterns(String lookupPath) {
|
||||
List<String> matches = new ArrayList<>();
|
||||
for (String pattern : this.patterns) {
|
||||
String match = getMatchingPattern(pattern, lookupPath);
|
||||
if (match != null) {
|
||||
matches.add(match);
|
||||
}
|
||||
}
|
||||
Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
|
||||
return matches;
|
||||
}
|
||||
|
||||
private String getMatchingPattern(String pattern, String lookupPath) {
|
||||
if (pattern.equals(lookupPath)) {
|
||||
return pattern;
|
||||
}
|
||||
if (this.useSuffixPatternMatch) {
|
||||
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
|
||||
for (String extension : this.fileExtensions) {
|
||||
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
|
||||
return pattern + extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
boolean hasSuffix = pattern.indexOf('.') != -1;
|
||||
if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
|
||||
return pattern + ".*";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.pathMatcher.match(pattern, lookupPath)) {
|
||||
return pattern;
|
||||
}
|
||||
if (this.useTrailingSlashMatch) {
|
||||
if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
|
||||
return pattern +"/";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the two conditions based on the URL patterns they contain.
|
||||
* Patterns are compared one at a time, from top to bottom via
|
||||
* {@link PathMatcher#getPatternComparator(String)}. If all compared
|
||||
* patterns match equally, but one instance has more patterns, it is
|
||||
* considered a closer match.
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} to ensure they
|
||||
* contain only patterns that match the request and are sorted with
|
||||
* the best matches on top.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(PatternsRequestCondition other, ServerWebExchange exchange) {
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
|
||||
Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
|
||||
Iterator<String> iterator = this.patterns.iterator();
|
||||
Iterator<String> iteratorOther = other.patterns.iterator();
|
||||
while (iterator.hasNext() && iteratorOther.hasNext()) {
|
||||
int result = patternComparator.compare(iterator.next(), iteratorOther.next());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (iterator.hasNext()) {
|
||||
return -1;
|
||||
}
|
||||
else if (iteratorOther.hasNext()) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 org.springframework.http.MediaType;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.reactive.accept.ContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A logical disjunction (' || ') request condition to match a request's 'Accept' header
|
||||
* to a list of media type expressions. Two kinds of media type expressions are
|
||||
* supported, which are described in {@link RequestMapping#produces()} and
|
||||
* {@link RequestMapping#headers()} where the header name is 'Accept'.
|
||||
* Regardless of which syntax is used, the semantics are the same.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class ProducesRequestCondition extends AbstractRequestCondition<ProducesRequestCondition> {
|
||||
|
||||
// private final static ProducesRequestCondition PRE_FLIGHT_MATCH = new ProducesRequestCondition();
|
||||
|
||||
|
||||
private final List<ProduceMediaTypeExpression> MEDIA_TYPE_ALL_LIST =
|
||||
Collections.singletonList(new ProduceMediaTypeExpression("*/*"));
|
||||
|
||||
private final List<ProduceMediaTypeExpression> expressions;
|
||||
|
||||
private final ContentTypeResolver contentTypeResolver;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance from "produces" expressions. If 0 expressions
|
||||
* are provided in total, this condition will match to any request.
|
||||
* @param produces expressions with syntax defined by {@link RequestMapping#produces()}
|
||||
*/
|
||||
public ProducesRequestCondition(String... produces) {
|
||||
this(produces, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with "produces" and "header" expressions. "Header"
|
||||
* expressions where the header name is not 'Accept' or have no header value
|
||||
* defined are ignored. If 0 expressions are provided in total, this condition
|
||||
* will match to any request.
|
||||
* @param produces expressions with syntax defined by {@link RequestMapping#produces()}
|
||||
* @param headers expressions with syntax defined by {@link RequestMapping#headers()}
|
||||
*/
|
||||
public ProducesRequestCondition(String[] produces, String[] headers) {
|
||||
this(produces, headers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #ProducesRequestCondition(String[], String[])} but also
|
||||
* accepting a {@link ContentNegotiationManager}.
|
||||
* @param produces expressions with syntax defined by {@link RequestMapping#produces()}
|
||||
* @param headers expressions with syntax defined by {@link RequestMapping#headers()}
|
||||
* @param resolver used to determine requested content type
|
||||
*/
|
||||
public ProducesRequestCondition(String[] produces, String[] headers, ContentTypeResolver resolver) {
|
||||
this.expressions = new ArrayList<>(parseExpressions(produces, headers));
|
||||
Collections.sort(this.expressions);
|
||||
this.contentTypeResolver = (resolver != null ? resolver : new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor with already parsed media type expressions.
|
||||
*/
|
||||
private ProducesRequestCondition(Collection<ProduceMediaTypeExpression> expressions,
|
||||
ContentTypeResolver resolver) {
|
||||
|
||||
this.expressions = new ArrayList<>(expressions);
|
||||
Collections.sort(this.expressions);
|
||||
this.contentTypeResolver = (resolver != null ? resolver : new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
|
||||
private Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, String[] headers) {
|
||||
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeadersRequestCondition.HeaderExpression expr = new HeadersRequestCondition.HeaderExpression(header);
|
||||
if ("Accept".equalsIgnoreCase(expr.name)) {
|
||||
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
|
||||
result.add(new ProduceMediaTypeExpression(mediaType, expr.isNegated));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (produces != null) {
|
||||
for (String produce : produces) {
|
||||
result.add(new ProduceMediaTypeExpression(produce));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contained "produces" expressions.
|
||||
*/
|
||||
public Set<MediaTypeExpression> getExpressions() {
|
||||
return new LinkedHashSet<>(this.expressions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contained producible media types excluding negated expressions.
|
||||
*/
|
||||
public Set<MediaType> getProducibleMediaTypes() {
|
||||
Set<MediaType> result = new LinkedHashSet<>();
|
||||
for (ProduceMediaTypeExpression expression : this.expressions) {
|
||||
if (!expression.isNegated()) {
|
||||
result.add(expression.getMediaType());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the condition has any media type expressions.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.expressions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ProduceMediaTypeExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "other" instance if it has any expressions; returns "this"
|
||||
* instance otherwise. Practically that means a method-level "produces"
|
||||
* overrides a type-level "produces" condition.
|
||||
*/
|
||||
@Override
|
||||
public ProducesRequestCondition combine(ProducesRequestCondition other) {
|
||||
return (!other.expressions.isEmpty() ? other : this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any of the contained media type expressions match the given
|
||||
* request 'Content-Type' header and returns an instance that is guaranteed
|
||||
* to contain matching expressions only. The match is performed via
|
||||
* {@link MediaType#isCompatibleWith(MediaType)}.
|
||||
* @param exchange the current exchange
|
||||
* @return the same instance if there are no expressions;
|
||||
* or a new condition with matching expressions;
|
||||
* or {@code null} if no expressions match.
|
||||
*/
|
||||
@Override
|
||||
public ProducesRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
// if (CorsUtils.isPreFlightRequest(request)) {
|
||||
// return PRE_FLIGHT_MATCH;
|
||||
// }
|
||||
if (isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>(expressions);
|
||||
for (Iterator<ProduceMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
|
||||
ProduceMediaTypeExpression expression = iterator.next();
|
||||
if (!expression.match(exchange)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return (result.isEmpty()) ? null : new ProducesRequestCondition(result, this.contentTypeResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this and another "produces" condition as follows:
|
||||
* <ol>
|
||||
* <li>Sort 'Accept' header media types by quality value via
|
||||
* {@link MediaType#sortByQualityValue(List)} and iterate the list.
|
||||
* <li>Get the first index of matching media types in each "produces"
|
||||
* condition first matching with {@link MediaType#equals(Object)} and
|
||||
* then with {@link MediaType#includes(MediaType)}.
|
||||
* <li>If a lower index is found, the condition at that index wins.
|
||||
* <li>If both indexes are equal, the media types at the index are
|
||||
* compared further with {@link MediaType#SPECIFICITY_COMPARATOR}.
|
||||
* </ol>
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} and each instance
|
||||
* contains the matching producible media type expression only or
|
||||
* is otherwise empty.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(ProducesRequestCondition other, ServerWebExchange exchange) {
|
||||
try {
|
||||
List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(exchange);
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
int thisIndex = this.indexOfEqualMediaType(acceptedMediaType);
|
||||
int otherIndex = other.indexOfEqualMediaType(acceptedMediaType);
|
||||
int result = compareMatchingMediaTypes(this, thisIndex, other, otherIndex);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
thisIndex = this.indexOfIncludedMediaType(acceptedMediaType);
|
||||
otherIndex = other.indexOfIncludedMediaType(acceptedMediaType);
|
||||
result = compareMatchingMediaTypes(this, thisIndex, other, otherIndex);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (HttpMediaTypeNotAcceptableException ex) {
|
||||
// should never happen
|
||||
throw new IllegalStateException("Cannot compare without having any requested media types", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<MediaType> getAcceptedMediaTypes(ServerWebExchange exchange)
|
||||
throws HttpMediaTypeNotAcceptableException {
|
||||
|
||||
List<MediaType> mediaTypes = this.contentTypeResolver.resolveMediaTypes(exchange);
|
||||
return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes;
|
||||
}
|
||||
|
||||
private int indexOfEqualMediaType(MediaType mediaType) {
|
||||
for (int i = 0; i < getExpressionsToCompare().size(); i++) {
|
||||
MediaType currentMediaType = getExpressionsToCompare().get(i).getMediaType();
|
||||
if (mediaType.getType().equalsIgnoreCase(currentMediaType.getType()) &&
|
||||
mediaType.getSubtype().equalsIgnoreCase(currentMediaType.getSubtype())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int indexOfIncludedMediaType(MediaType mediaType) {
|
||||
for (int i = 0; i < getExpressionsToCompare().size(); i++) {
|
||||
if (mediaType.includes(getExpressionsToCompare().get(i).getMediaType())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int compareMatchingMediaTypes(ProducesRequestCondition condition1, int index1,
|
||||
ProducesRequestCondition condition2, int index2) {
|
||||
|
||||
int result = 0;
|
||||
if (index1 != index2) {
|
||||
result = index2 - index1;
|
||||
}
|
||||
else if (index1 != -1) {
|
||||
ProduceMediaTypeExpression expr1 = condition1.getExpressionsToCompare().get(index1);
|
||||
ProduceMediaTypeExpression expr2 = condition2.getExpressionsToCompare().get(index2);
|
||||
result = expr1.compareTo(expr2);
|
||||
result = (result != 0) ? result : expr1.getMediaType().compareTo(expr2.getMediaType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contained "produces" expressions or if that's empty, a list
|
||||
* with a {@code MediaType_ALL} expression.
|
||||
*/
|
||||
private List<ProduceMediaTypeExpression> getExpressionsToCompare() {
|
||||
return (this.expressions.isEmpty() ? MEDIA_TYPE_ALL_LIST : this.expressions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses and matches a single media type expression to a request's 'Accept' header.
|
||||
*/
|
||||
class ProduceMediaTypeExpression extends AbstractMediaTypeExpression {
|
||||
|
||||
ProduceMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
super(mediaType, negated);
|
||||
}
|
||||
|
||||
ProduceMediaTypeExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchMediaType(ServerWebExchange exchange) throws HttpMediaTypeNotAcceptableException {
|
||||
List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(exchange);
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
if (getMediaType().isCompatibleWith(acceptedMediaType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Contract for request mapping conditions.
|
||||
*
|
||||
* <p>Request conditions can be combined via {@link #combine(Object)}, matched to
|
||||
* a request via {@link #getMatchingCondition(ServerWebExchange)}, and compared
|
||||
* to each other via {@link #compareTo(Object, ServerWebExchange)} to determine
|
||||
* which is a closer match for a given request.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @param <T> the type of objects that this RequestCondition can be combined
|
||||
* with and compared to
|
||||
*/
|
||||
public interface RequestCondition<T> {
|
||||
|
||||
/**
|
||||
* Combine this condition with another such as conditions from a
|
||||
* type-level and method-level {@code @RequestMapping} annotation.
|
||||
* @param other the condition to combine with.
|
||||
* @return a request condition instance that is the result of combining
|
||||
* the two condition instances.
|
||||
*/
|
||||
T combine(T other);
|
||||
|
||||
/**
|
||||
* Check if the condition matches the request returning a potentially new
|
||||
* instance created for the current request. For example a condition with
|
||||
* multiple URL patterns may return a new instance only with those patterns
|
||||
* that match the request.
|
||||
* <p>For CORS pre-flight requests, conditions should match to the would-be,
|
||||
* actual request (e.g. URL pattern, query parameters, and the HTTP method
|
||||
* from the "Access-Control-Request-Method" header). If a condition cannot
|
||||
* be matched to a pre-flight request it should return an instance with
|
||||
* empty content thus not causing a failure to match.
|
||||
* @return a condition instance in case of a match or {@code null} otherwise.
|
||||
*/
|
||||
T getMatchingCondition(ServerWebExchange exchange);
|
||||
|
||||
/**
|
||||
* Compare this condition to another condition in the context of
|
||||
* a specific request. This method assumes both instances have
|
||||
* been obtained via {@link #getMatchingCondition(ServerWebExchange)}
|
||||
* to ensure they have content relevant to current request only.
|
||||
*/
|
||||
int compareTo(T other, ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A holder for a {@link RequestCondition} useful when the type of the request
|
||||
* condition is not known ahead of time, e.g. custom condition. Since this
|
||||
* class is also an implementation of {@code RequestCondition}, effectively it
|
||||
* decorates the held request condition and allows it to be combined and compared
|
||||
* with other request conditions in a type and null safe way.
|
||||
*
|
||||
* <p>When two {@code RequestConditionHolder} instances are combined or compared
|
||||
* with each other, it is expected the conditions they hold are of the same type.
|
||||
* If they are not, a {@link ClassCastException} is raised.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class RequestConditionHolder extends AbstractRequestCondition<RequestConditionHolder> {
|
||||
|
||||
private final RequestCondition<Object> condition;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new holder to wrap the given request condition.
|
||||
* @param requestCondition the condition to hold, may be {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public RequestConditionHolder(RequestCondition<?> requestCondition) {
|
||||
this.condition = (RequestCondition<Object>) requestCondition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the held request condition, or {@code null} if not holding one.
|
||||
*/
|
||||
public RequestCondition<?> getCondition() {
|
||||
return this.condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<?> getContent() {
|
||||
return (this.condition != null ? Collections.singleton(this.condition) : Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the request conditions held by the two RequestConditionHolder
|
||||
* instances after making sure the conditions are of the same type.
|
||||
* Or if one holder is empty, the other holder is returned.
|
||||
*/
|
||||
@Override
|
||||
public RequestConditionHolder combine(RequestConditionHolder other) {
|
||||
if (this.condition == null && other.condition == null) {
|
||||
return this;
|
||||
}
|
||||
else if (this.condition == null) {
|
||||
return other;
|
||||
}
|
||||
else if (other.condition == null) {
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
assertEqualConditionTypes(other);
|
||||
RequestCondition<?> combined = (RequestCondition<?>) this.condition.combine(other.condition);
|
||||
return new RequestConditionHolder(combined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the held request conditions are of the same type.
|
||||
*/
|
||||
private void assertEqualConditionTypes(RequestConditionHolder other) {
|
||||
Class<?> clazz = this.condition.getClass();
|
||||
Class<?> otherClazz = other.condition.getClass();
|
||||
if (!clazz.equals(otherClazz)) {
|
||||
throw new ClassCastException("Incompatible request conditions: " + clazz + " and " + otherClazz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the matching condition for the held request condition wrap it in a
|
||||
* new RequestConditionHolder instance. Or otherwise if this is an empty
|
||||
* holder, return the same holder instance.
|
||||
*/
|
||||
@Override
|
||||
public RequestConditionHolder getMatchingCondition(ServerWebExchange exchange) {
|
||||
if (this.condition == null) {
|
||||
return this;
|
||||
}
|
||||
RequestCondition<?> match = (RequestCondition<?>) this.condition.getMatchingCondition(exchange);
|
||||
return (match != null ? new RequestConditionHolder(match) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the request conditions held by the two RequestConditionHolder
|
||||
* instances after making sure the conditions are of the same type.
|
||||
* Or if one holder is empty, the other holder is preferred.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(RequestConditionHolder other, ServerWebExchange exchange) {
|
||||
if (this.condition == null && other.condition == null) {
|
||||
return 0;
|
||||
}
|
||||
else if (this.condition == null) {
|
||||
return 1;
|
||||
}
|
||||
else if (other.condition == null) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
assertEqualConditionTypes(other);
|
||||
return this.condition.compareTo(other.condition, exchange);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.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.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A logical disjunction (' || ') request condition that matches a request
|
||||
* against a set of {@link RequestMethod}s.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class RequestMethodsRequestCondition extends AbstractRequestCondition<RequestMethodsRequestCondition> {
|
||||
|
||||
private static final RequestMethodsRequestCondition HEAD_CONDITION =
|
||||
new RequestMethodsRequestCondition(RequestMethod.HEAD);
|
||||
|
||||
|
||||
private final Set<RequestMethod> methods;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance with the given request methods.
|
||||
* @param requestMethods 0 or more HTTP request methods;
|
||||
* if, 0 the condition will match to every request
|
||||
*/
|
||||
public RequestMethodsRequestCondition(RequestMethod... requestMethods) {
|
||||
this(asList(requestMethods));
|
||||
}
|
||||
|
||||
private RequestMethodsRequestCondition(Collection<RequestMethod> requestMethods) {
|
||||
this.methods = Collections.unmodifiableSet(new LinkedHashSet<>(requestMethods));
|
||||
}
|
||||
|
||||
|
||||
private static List<RequestMethod> asList(RequestMethod... requestMethods) {
|
||||
return (requestMethods != null ? Arrays.asList(requestMethods) : Collections.emptyList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all {@link RequestMethod}s contained in this condition.
|
||||
*/
|
||||
public Set<RequestMethod> getMethods() {
|
||||
return this.methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<RequestMethod> getContent() {
|
||||
return this.methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance with a union of the HTTP request methods
|
||||
* from "this" and the "other" instance.
|
||||
*/
|
||||
@Override
|
||||
public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) {
|
||||
Set<RequestMethod> set = new LinkedHashSet<>(this.methods);
|
||||
set.addAll(other.methods);
|
||||
return new RequestMethodsRequestCondition(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any of the HTTP request methods match the given request and
|
||||
* return an instance that contains the matching HTTP request method only.
|
||||
* @param exchange the current exchange
|
||||
* @return the same instance if the condition is empty (unless the request
|
||||
* method is HTTP OPTIONS), a new condition with the matched request method,
|
||||
* or {@code null} if there is no match or the condition is empty and the
|
||||
* request method is OPTIONS.
|
||||
*/
|
||||
@Override
|
||||
public RequestMethodsRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
// if (CorsUtils.isPreFlightRequest(request)) {
|
||||
// return matchPreFlight(request);
|
||||
// }
|
||||
if (getMethods().isEmpty()) {
|
||||
if (RequestMethod.OPTIONS.name().equals(exchange.getRequest().getMethod().name())) {
|
||||
return null; // No implicit match for OPTIONS (we handle it)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
return matchRequestMethod(exchange.getRequest().getMethod().name());
|
||||
}
|
||||
|
||||
/**
|
||||
* On a pre-flight request match to the would-be, actual request.
|
||||
* Hence empty conditions is a match, otherwise try to match to the HTTP
|
||||
* method in the "Access-Control-Request-Method" header.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private RequestMethodsRequestCondition matchPreFlight(HttpServletRequest request) {
|
||||
if (getMethods().isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
String expectedMethod = request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
|
||||
return matchRequestMethod(expectedMethod);
|
||||
}
|
||||
|
||||
private RequestMethodsRequestCondition matchRequestMethod(String httpMethodValue) {
|
||||
HttpMethod httpMethod = HttpMethod.resolve(httpMethodValue);
|
||||
if (httpMethod != null) {
|
||||
for (RequestMethod method : getMethods()) {
|
||||
if (httpMethod.matches(method.name())) {
|
||||
return new RequestMethodsRequestCondition(method);
|
||||
}
|
||||
}
|
||||
if (httpMethod == HttpMethod.HEAD && getMethods().contains(RequestMethod.GET)) {
|
||||
return HEAD_CONDITION;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* <ul>
|
||||
* <li>0 if the two conditions contain the same number of HTTP request methods
|
||||
* <li>Less than 0 if "this" instance has an HTTP request method but "other" doesn't
|
||||
* <li>Greater than 0 "other" has an HTTP request method but "this" doesn't
|
||||
* </ul>
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} and therefore each instance
|
||||
* contains the matching HTTP request method only or is otherwise empty.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(RequestMethodsRequestCondition other, ServerWebExchange exchange) {
|
||||
return (other.methods.size() - this.methods.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Support for mapping requests based on a
|
||||
* {@link org.springframework.web.reactive.result.condition.RequestCondition
|
||||
* RequestCondition} type hierarchy.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.condition;
|
||||
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.method;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.reactive.accept.ContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.FileExtensionContentTypeResolver;
|
||||
import org.springframework.web.reactive.result.condition.ConsumesRequestCondition;
|
||||
import org.springframework.web.reactive.result.condition.HeadersRequestCondition;
|
||||
import org.springframework.web.reactive.result.condition.ParamsRequestCondition;
|
||||
import org.springframework.web.reactive.result.condition.PatternsRequestCondition;
|
||||
import org.springframework.web.reactive.result.condition.ProducesRequestCondition;
|
||||
import org.springframework.web.reactive.result.condition.RequestCondition;
|
||||
import org.springframework.web.reactive.result.condition.RequestConditionHolder;
|
||||
import org.springframework.web.reactive.result.condition.RequestMethodsRequestCondition;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.HttpRequestPathHelper;
|
||||
|
||||
/**
|
||||
* Encapsulates the following request mapping conditions:
|
||||
* <ol>
|
||||
* <li>{@link PatternsRequestCondition}
|
||||
* <li>{@link RequestMethodsRequestCondition}
|
||||
* <li>{@link ParamsRequestCondition}
|
||||
* <li>{@link HeadersRequestCondition}
|
||||
* <li>{@link ConsumesRequestCondition}
|
||||
* <li>{@link ProducesRequestCondition}
|
||||
* <li>{@code RequestCondition} (optional, custom request condition)
|
||||
* </ol>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final PatternsRequestCondition patternsCondition;
|
||||
|
||||
private final RequestMethodsRequestCondition methodsCondition;
|
||||
|
||||
private final ParamsRequestCondition paramsCondition;
|
||||
|
||||
private final HeadersRequestCondition headersCondition;
|
||||
|
||||
private final ConsumesRequestCondition consumesCondition;
|
||||
|
||||
private final ProducesRequestCondition producesCondition;
|
||||
|
||||
private final RequestConditionHolder customConditionHolder;
|
||||
|
||||
|
||||
public RequestMappingInfo(String name, PatternsRequestCondition patterns, RequestMethodsRequestCondition methods,
|
||||
ParamsRequestCondition params, HeadersRequestCondition headers, ConsumesRequestCondition consumes,
|
||||
ProducesRequestCondition produces, RequestCondition<?> custom) {
|
||||
|
||||
this.name = (StringUtils.hasText(name) ? name : null);
|
||||
this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition());
|
||||
this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition());
|
||||
this.paramsCondition = (params != null ? params : new ParamsRequestCondition());
|
||||
this.headersCondition = (headers != null ? headers : new HeadersRequestCondition());
|
||||
this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition());
|
||||
this.producesCondition = (produces != null ? produces : new ProducesRequestCondition());
|
||||
this.customConditionHolder = new RequestConditionHolder(custom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given request conditions.
|
||||
*/
|
||||
public RequestMappingInfo(PatternsRequestCondition patterns, RequestMethodsRequestCondition methods,
|
||||
ParamsRequestCondition params, HeadersRequestCondition headers, ConsumesRequestCondition consumes,
|
||||
ProducesRequestCondition produces, RequestCondition<?> custom) {
|
||||
|
||||
this(null, patterns, methods, params, headers, consumes, produces, custom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-create a RequestMappingInfo with the given custom request condition.
|
||||
*/
|
||||
public RequestMappingInfo(RequestMappingInfo info, RequestCondition<?> customRequestCondition) {
|
||||
this(info.name, info.patternsCondition, info.methodsCondition, info.paramsCondition, info.headersCondition,
|
||||
info.consumesCondition, info.producesCondition, customRequestCondition);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name for this mapping, or {@code null}.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL patterns of this {@link RequestMappingInfo};
|
||||
* or instance with 0 patterns, never {@code null}.
|
||||
*/
|
||||
public PatternsRequestCondition getPatternsCondition() {
|
||||
return this.patternsCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP request methods of this {@link RequestMappingInfo};
|
||||
* or instance with 0 request methods, never {@code null}.
|
||||
*/
|
||||
public RequestMethodsRequestCondition getMethodsCondition() {
|
||||
return this.methodsCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "parameters" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 parameter expressions, never {@code null}.
|
||||
*/
|
||||
public ParamsRequestCondition getParamsCondition() {
|
||||
return this.paramsCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "headers" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 header expressions, never {@code null}.
|
||||
*/
|
||||
public HeadersRequestCondition getHeadersCondition() {
|
||||
return this.headersCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "consumes" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 consumes expressions, never {@code null}.
|
||||
*/
|
||||
public ConsumesRequestCondition getConsumesCondition() {
|
||||
return this.consumesCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "produces" condition of this {@link RequestMappingInfo};
|
||||
* or instance with 0 produces expressions, never {@code null}.
|
||||
*/
|
||||
public ProducesRequestCondition getProducesCondition() {
|
||||
return this.producesCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "custom" condition of this {@link RequestMappingInfo}; or {@code null}.
|
||||
*/
|
||||
public RequestCondition<?> getCustomCondition() {
|
||||
return this.customConditionHolder.getCondition();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Combines "this" request mapping info (i.e. the current instance) with another request mapping info instance.
|
||||
* <p>Example: combine type- and method-level request mappings.
|
||||
* @return a new request mapping info instance; never {@code null}
|
||||
*/
|
||||
@Override
|
||||
public RequestMappingInfo combine(RequestMappingInfo other) {
|
||||
String name = combineNames(other);
|
||||
PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
|
||||
RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
|
||||
ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
|
||||
HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
|
||||
ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
|
||||
ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
|
||||
RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
|
||||
|
||||
return new RequestMappingInfo(name, patterns,
|
||||
methods, params, headers, consumes, produces, custom.getCondition());
|
||||
}
|
||||
|
||||
private String combineNames(RequestMappingInfo other) {
|
||||
if (this.name != null && other.name != null) {
|
||||
String separator = "#";
|
||||
return this.name + separator + other.name;
|
||||
}
|
||||
else if (this.name != null) {
|
||||
return this.name;
|
||||
}
|
||||
else {
|
||||
return (other.name != null ? other.name : null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all conditions in this request mapping info match the provided request and returns
|
||||
* a potentially new request mapping info with conditions tailored to the current request.
|
||||
* <p>For example the returned instance may contain the subset of URL patterns that match to
|
||||
* the current request, sorted with best matching patterns on top.
|
||||
* @return a new instance in case all conditions match; or {@code null} otherwise
|
||||
*/
|
||||
@Override
|
||||
public RequestMappingInfo getMatchingCondition(ServerWebExchange exchange) {
|
||||
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(exchange);
|
||||
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(exchange);
|
||||
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(exchange);
|
||||
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(exchange);
|
||||
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(exchange);
|
||||
|
||||
if (methods == null || params == null || headers == null || consumes == null || produces == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(exchange);
|
||||
if (patterns == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(exchange);
|
||||
if (custom == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RequestMappingInfo(this.name, patterns,
|
||||
methods, params, headers, consumes, produces, custom.getCondition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares "this" info (i.e. the current instance) with another info in the context of a request.
|
||||
* <p>Note: It is assumed both instances have been obtained via
|
||||
* {@link #getMatchingCondition(ServerWebExchange)} to ensure they have conditions with
|
||||
* content relevant to current request.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(RequestMappingInfo other, ServerWebExchange exchange) {
|
||||
int result = this.patternsCondition.compareTo(other.getPatternsCondition(), exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = this.paramsCondition.compareTo(other.getParamsCondition(), exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = this.headersCondition.compareTo(other.getHeadersCondition(), exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = this.consumesCondition.compareTo(other.getConsumesCondition(), exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = this.producesCondition.compareTo(other.getProducesCondition(), exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = this.methodsCondition.compareTo(other.getMethodsCondition(), exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = this.customConditionHolder.compareTo(other.customConditionHolder, exchange);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof RequestMappingInfo)) {
|
||||
return false;
|
||||
}
|
||||
RequestMappingInfo otherInfo = (RequestMappingInfo) other;
|
||||
return (this.patternsCondition.equals(otherInfo.patternsCondition) &&
|
||||
this.methodsCondition.equals(otherInfo.methodsCondition) &&
|
||||
this.paramsCondition.equals(otherInfo.paramsCondition) &&
|
||||
this.headersCondition.equals(otherInfo.headersCondition) &&
|
||||
this.consumesCondition.equals(otherInfo.consumesCondition) &&
|
||||
this.producesCondition.equals(otherInfo.producesCondition) &&
|
||||
this.customConditionHolder.equals(otherInfo.customConditionHolder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (this.patternsCondition.hashCode() * 31 + // primary differentiation
|
||||
this.methodsCondition.hashCode() + this.paramsCondition.hashCode() +
|
||||
this.headersCondition.hashCode() + this.consumesCondition.hashCode() +
|
||||
this.producesCondition.hashCode() + this.customConditionHolder.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("{");
|
||||
builder.append(this.patternsCondition);
|
||||
if (!this.methodsCondition.isEmpty()) {
|
||||
builder.append(",methods=").append(this.methodsCondition);
|
||||
}
|
||||
if (!this.paramsCondition.isEmpty()) {
|
||||
builder.append(",params=").append(this.paramsCondition);
|
||||
}
|
||||
if (!this.headersCondition.isEmpty()) {
|
||||
builder.append(",headers=").append(this.headersCondition);
|
||||
}
|
||||
if (!this.consumesCondition.isEmpty()) {
|
||||
builder.append(",consumes=").append(this.consumesCondition);
|
||||
}
|
||||
if (!this.producesCondition.isEmpty()) {
|
||||
builder.append(",produces=").append(this.producesCondition);
|
||||
}
|
||||
if (!this.customConditionHolder.isEmpty()) {
|
||||
builder.append(",custom=").append(this.customConditionHolder);
|
||||
}
|
||||
builder.append('}');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code RequestMappingInfo.Builder} with the given paths.
|
||||
* @param paths the paths to use
|
||||
*/
|
||||
public static Builder paths(String... paths) {
|
||||
return new DefaultBuilder(paths);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines a builder for creating a RequestMappingInfo.
|
||||
*/
|
||||
public interface Builder {
|
||||
|
||||
/**
|
||||
* Set the path patterns.
|
||||
*/
|
||||
Builder paths(String... paths);
|
||||
|
||||
/**
|
||||
* Set the request method conditions.
|
||||
*/
|
||||
Builder methods(RequestMethod... methods);
|
||||
|
||||
/**
|
||||
* Set the request param conditions.
|
||||
*/
|
||||
Builder params(String... params);
|
||||
|
||||
/**
|
||||
* Set the header conditions.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
Builder headers(String... headers);
|
||||
|
||||
/**
|
||||
* Set the consumes conditions.
|
||||
*/
|
||||
Builder consumes(String... consumes);
|
||||
|
||||
/**
|
||||
* Set the produces conditions.
|
||||
*/
|
||||
Builder produces(String... produces);
|
||||
|
||||
/**
|
||||
* Set the mapping name.
|
||||
*/
|
||||
Builder mappingName(String name);
|
||||
|
||||
/**
|
||||
* Set a custom condition to use.
|
||||
*/
|
||||
Builder customCondition(RequestCondition<?> condition);
|
||||
|
||||
/**
|
||||
* Provide additional configuration needed for request mapping purposes.
|
||||
*/
|
||||
Builder options(BuilderConfiguration options);
|
||||
|
||||
/**
|
||||
* Build the RequestMappingInfo.
|
||||
*/
|
||||
RequestMappingInfo build();
|
||||
}
|
||||
|
||||
|
||||
private static class DefaultBuilder implements Builder {
|
||||
|
||||
private String[] paths;
|
||||
|
||||
private RequestMethod[] methods;
|
||||
|
||||
private String[] params;
|
||||
|
||||
private String[] headers;
|
||||
|
||||
private String[] consumes;
|
||||
|
||||
private String[] produces;
|
||||
|
||||
private String mappingName;
|
||||
|
||||
private RequestCondition<?> customCondition;
|
||||
|
||||
private BuilderConfiguration options = new BuilderConfiguration();
|
||||
|
||||
public DefaultBuilder(String... paths) {
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder paths(String... paths) {
|
||||
this.paths = paths;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder methods(RequestMethod... methods) {
|
||||
this.methods = methods;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder params(String... params) {
|
||||
this.params = params;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder headers(String... headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder consumes(String... consumes) {
|
||||
this.consumes = consumes;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder produces(String... produces) {
|
||||
this.produces = produces;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder mappingName(String name) {
|
||||
this.mappingName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultBuilder customCondition(RequestCondition<?> condition) {
|
||||
this.customCondition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder options(BuilderConfiguration options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestMappingInfo build() {
|
||||
ContentTypeResolver contentTypeResolver = this.options.getContentTypeResolver();
|
||||
|
||||
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
|
||||
this.paths, this.options.getPathHelper(), this.options.getPathMatcher(),
|
||||
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
|
||||
this.options.getFileExtensions());
|
||||
|
||||
return new RequestMappingInfo(this.mappingName, patternsCondition,
|
||||
new RequestMethodsRequestCondition(methods),
|
||||
new ParamsRequestCondition(this.params),
|
||||
new HeadersRequestCondition(this.headers),
|
||||
new ConsumesRequestCondition(this.consumes, this.headers),
|
||||
new ProducesRequestCondition(this.produces, this.headers, contentTypeResolver),
|
||||
this.customCondition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Container for configuration options used for request mapping purposes.
|
||||
* Such configuration is required to create RequestMappingInfo instances but
|
||||
* is typically used across all RequestMappingInfo instances.
|
||||
* @see Builder#options
|
||||
*/
|
||||
public static class BuilderConfiguration {
|
||||
|
||||
private HttpRequestPathHelper pathHelper;
|
||||
|
||||
private PathMatcher pathMatcher;
|
||||
|
||||
private boolean trailingSlashMatch = true;
|
||||
|
||||
private boolean suffixPatternMatch = true;
|
||||
|
||||
private boolean registeredSuffixPatternMatch = false;
|
||||
|
||||
private ContentTypeResolver contentTypeResolver;
|
||||
|
||||
/**
|
||||
* Set a custom UrlPathHelper to use for the PatternsRequestCondition.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public void setPathHelper(HttpRequestPathHelper pathHelper) {
|
||||
this.pathHelper = pathHelper;
|
||||
}
|
||||
|
||||
public HttpRequestPathHelper getPathHelper() {
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom PathMatcher to use for the PatternsRequestCondition.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
public PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to apply trailing slash matching in PatternsRequestCondition.
|
||||
* <p>By default this is set to 'true'.
|
||||
*/
|
||||
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
|
||||
this.trailingSlashMatch = trailingSlashMatch;
|
||||
}
|
||||
|
||||
public boolean useTrailingSlashMatch() {
|
||||
return this.trailingSlashMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to apply suffix pattern matching in PatternsRequestCondition.
|
||||
* <p>By default this is set to 'true'.
|
||||
* @see #setRegisteredSuffixPatternMatch(boolean)
|
||||
*/
|
||||
public void setSuffixPatternMatch(boolean suffixPatternMatch) {
|
||||
this.suffixPatternMatch = suffixPatternMatch;
|
||||
}
|
||||
|
||||
public boolean useSuffixPatternMatch() {
|
||||
return this.suffixPatternMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether suffix pattern matching should be restricted to registered
|
||||
* file extensions only. Setting this property also sets
|
||||
* suffixPatternMatch=true and requires that a
|
||||
* {@link #setContentTypeResolver} is also configured in order to
|
||||
* obtain the registered file extensions.
|
||||
*/
|
||||
public void setRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch) {
|
||||
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
|
||||
this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
|
||||
}
|
||||
|
||||
public boolean useRegisteredSuffixPatternMatch() {
|
||||
return this.registeredSuffixPatternMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file extensions to use for suffix pattern matching. If
|
||||
* {@code registeredSuffixPatternMatch=true}, the extensions are obtained
|
||||
* from the configured {@code contentTypeResolver}.
|
||||
*/
|
||||
public List<String> getFileExtensions() {
|
||||
ContentTypeResolver resolver = getContentTypeResolver();
|
||||
if (useRegisteredSuffixPatternMatch() && resolver != null) {
|
||||
if (resolver instanceof FileExtensionContentTypeResolver) {
|
||||
return ((FileExtensionContentTypeResolver) resolver).getAllFileExtensions();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ContentNegotiationManager to use for the ProducesRequestCondition.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public void setContentTypeResolver(ContentTypeResolver resolver) {
|
||||
this.contentTypeResolver = resolver;
|
||||
}
|
||||
|
||||
public ContentTypeResolver getContentTypeResolver() {
|
||||
return this.contentTypeResolver;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CompositeRequestCondition}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class CompositeRequestConditionTests {
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
private ServerHttpRequest request;
|
||||
|
||||
private ParamsRequestCondition param1;
|
||||
private ParamsRequestCondition param2;
|
||||
private ParamsRequestCondition param3;
|
||||
|
||||
private HeadersRequestCondition header1;
|
||||
private HeadersRequestCondition header2;
|
||||
private HeadersRequestCondition header3;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
|
||||
this.param1 = new ParamsRequestCondition("param1");
|
||||
this.param2 = new ParamsRequestCondition("param2");
|
||||
this.param3 = this.param1.combine(this.param2);
|
||||
|
||||
this.header1 = new HeadersRequestCondition("header1");
|
||||
this.header2 = new HeadersRequestCondition("header2");
|
||||
this.header3 = this.header1.combine(this.header2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void combine() {
|
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1, this.header1);
|
||||
CompositeRequestCondition cond2 = new CompositeRequestCondition(this.param2, this.header2);
|
||||
CompositeRequestCondition cond3 = new CompositeRequestCondition(this.param3, this.header3);
|
||||
|
||||
assertEquals(cond3, cond1.combine(cond2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineEmpty() {
|
||||
CompositeRequestCondition empty = new CompositeRequestCondition();
|
||||
CompositeRequestCondition notEmpty = new CompositeRequestCondition(this.param1);
|
||||
|
||||
assertSame(empty, empty.combine(empty));
|
||||
assertSame(notEmpty, notEmpty.combine(empty));
|
||||
assertSame(notEmpty, empty.combine(notEmpty));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void combineDifferentLength() {
|
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1);
|
||||
CompositeRequestCondition cond2 = new CompositeRequestCondition(this.param1, this.header1);
|
||||
cond1.combine(cond2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void match() {
|
||||
this.request.getQueryParams().add("param1", "paramValue1");
|
||||
|
||||
RequestCondition<?> condition1 = new RequestMethodsRequestCondition(RequestMethod.GET, RequestMethod.POST);
|
||||
RequestCondition<?> condition2 = new RequestMethodsRequestCondition(RequestMethod.GET);
|
||||
|
||||
CompositeRequestCondition composite1 = new CompositeRequestCondition(this.param1, condition1);
|
||||
CompositeRequestCondition composite2 = new CompositeRequestCondition(this.param1, condition2);
|
||||
|
||||
assertEquals(composite2, composite1.getMatchingCondition(this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMatch() {
|
||||
CompositeRequestCondition cond = new CompositeRequestCondition(this.param1);
|
||||
assertNull(cond.getMatchingCondition(this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchEmpty() {
|
||||
CompositeRequestCondition empty = new CompositeRequestCondition();
|
||||
assertSame(empty, empty.getMatchingCondition(this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare() {
|
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1);
|
||||
CompositeRequestCondition cond3 = new CompositeRequestCondition(this.param3);
|
||||
|
||||
assertEquals(1, cond1.compareTo(cond3, this.exchange));
|
||||
assertEquals(-1, cond3.compareTo(cond1, this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareEmpty() {
|
||||
CompositeRequestCondition empty = new CompositeRequestCondition();
|
||||
CompositeRequestCondition notEmpty = new CompositeRequestCondition(this.param1);
|
||||
|
||||
assertEquals(0, empty.compareTo(empty, this.exchange));
|
||||
assertEquals(-1, notEmpty.compareTo(empty, this.exchange));
|
||||
assertEquals(1, empty.compareTo(notEmpty, this.exchange));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void compareDifferentLength() {
|
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1);
|
||||
CompositeRequestCondition cond2 = new CompositeRequestCondition(this.param1, this.header1);
|
||||
cond1.compareTo(cond2, this.exchange);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.reactive.result.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ConsumesRequestConditionTests {
|
||||
|
||||
@Test
|
||||
public void consumesMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negatedConsumesMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("!text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConsumableMediaTypesNegatedExpression() throws Exception {
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("!application/xml");
|
||||
assertEquals(Collections.emptySet(), condition.getConsumableMediaTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesWildcardMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/*");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesMultipleMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesSingleNoMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("application/xml");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesParseError() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("01");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesParseErrorWithNegation() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("01");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("!text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareToSingle() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
|
||||
ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain");
|
||||
ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareToMultiple() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
|
||||
ConsumesRequestCondition condition1 = new ConsumesRequestCondition("*/*", "text/plain");
|
||||
ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*", "text/plain;q=0.7");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void combine() throws Exception {
|
||||
ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain");
|
||||
ConsumesRequestCondition condition2 = new ConsumesRequestCondition("application/xml");
|
||||
|
||||
ConsumesRequestCondition result = condition1.combine(condition2);
|
||||
assertEquals(condition2, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineWithDefault() throws Exception {
|
||||
ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain");
|
||||
ConsumesRequestCondition condition2 = new ConsumesRequestCondition();
|
||||
|
||||
ConsumesRequestCondition result = condition1.combine(condition2);
|
||||
assertEquals(condition1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseConsumesAndHeaders() throws Exception {
|
||||
String[] consumes = new String[] {"text/plain"};
|
||||
String[] headers = new String[]{"foo=bar", "content-type=application/xml,application/pdf"};
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition(consumes, headers);
|
||||
|
||||
assertConditions(condition, "text/plain", "application/xml", "application/pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMatchingCondition() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml");
|
||||
|
||||
ConsumesRequestCondition result = condition.getMatchingCondition(exchange);
|
||||
assertConditions(result, "text/plain");
|
||||
|
||||
condition = new ConsumesRequestCondition("application/xml");
|
||||
result = condition.getMatchingCondition(exchange);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
private void assertConditions(ConsumesRequestCondition condition, String... expected) {
|
||||
Collection<ConsumeMediaTypeExpression> expressions = condition.getContent();
|
||||
assertEquals("Invalid amount of conditions", expressions.size(), expected.length);
|
||||
for (String s : expected) {
|
||||
boolean found = false;
|
||||
for (ConsumeMediaTypeExpression expr : expressions) {
|
||||
String conditionMediaType = expr.getMediaType().toString();
|
||||
if (conditionMediaType.equals(s)) {
|
||||
found = true;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fail("Condition [" + s + "] not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange() throws URISyntaxException {
|
||||
return createExchange(null);
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange(String contentType) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||
if (contentType != null) {
|
||||
request.getHeaders().add("Content-Type", contentType);
|
||||
}
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HeadersRequestCondition}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
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() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("Accept", "");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("accept");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerPresentNoMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("bar", "");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerNotPresent() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("!accept");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bar");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo=bar");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueNoMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bazz");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo=bar");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerCaseSensitiveValueMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bar");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo=Bar");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueMatchNegated() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "baz");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo!=bar");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueNoMatchNegated() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bar");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo!=bar");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
|
||||
HeadersRequestCondition condition1 = new HeadersRequestCondition("foo", "bar", "baz");
|
||||
HeadersRequestCondition condition2 = new HeadersRequestCondition("foo", "bar");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
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);
|
||||
Collection<?> conditions = result.getContent();
|
||||
assertEquals(2, conditions.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMatchingCondition() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bar");
|
||||
HeadersRequestCondition condition = new HeadersRequestCondition("foo");
|
||||
|
||||
HeadersRequestCondition result = condition.getMatchingCondition(exchange);
|
||||
assertEquals(condition, result);
|
||||
|
||||
condition = new HeadersRequestCondition("bar");
|
||||
|
||||
result = condition.getMatchingCondition(exchange);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
|
||||
private ServerWebExchange createExchange() throws URISyntaxException {
|
||||
return createExchange(null, null);
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange(String headerName, String headerValue) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||
if (headerName != null) {
|
||||
request.getHeaders().add(headerName, headerValue);
|
||||
}
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @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() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "");
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramPresentNoMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("bar", "");
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramNotPresent() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("!foo");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramValueMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bar");
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramValueNoMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bazz");
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
|
||||
ParamsRequestCondition condition1 = new ParamsRequestCondition("foo", "bar", "baz");
|
||||
ParamsRequestCondition condition2 = new ParamsRequestCondition("foo", "bar");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
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);
|
||||
Collection<?> conditions = result.getContent();
|
||||
assertEquals(2, conditions.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMatchingCondition() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("foo", "bar");
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo");
|
||||
|
||||
ParamsRequestCondition result = condition.getMatchingCondition(exchange);
|
||||
assertEquals(condition, result);
|
||||
|
||||
condition = new ParamsRequestCondition("bar");
|
||||
|
||||
result = condition.getMatchingCondition(exchange);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange() throws URISyntaxException {
|
||||
return createExchange(null, null);
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange(String paramName, String paramValue) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||
if (paramName != null) {
|
||||
request.getQueryParams().add(paramName, paramValue);
|
||||
}
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PatternsRequestCondition}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class PatternsRequestConditionTests {
|
||||
|
||||
@Test
|
||||
public void prependSlash() {
|
||||
PatternsRequestCondition c = new PatternsRequestCondition("foo");
|
||||
assertEquals("/foo", c.getPatterns().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prependNonEmptyPatternsOnly() {
|
||||
PatternsRequestCondition c = new PatternsRequestCondition("");
|
||||
assertEquals("Do not prepend empty patterns (SPR-8255)", "", c.getPatterns().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineEmptySets() {
|
||||
PatternsRequestCondition c1 = new PatternsRequestCondition();
|
||||
PatternsRequestCondition c2 = new PatternsRequestCondition();
|
||||
|
||||
assertEquals(new PatternsRequestCondition(""), c1.combine(c2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineOnePatternWithEmptySet() {
|
||||
PatternsRequestCondition c1 = new PatternsRequestCondition("/type1", "/type2");
|
||||
PatternsRequestCondition c2 = new PatternsRequestCondition();
|
||||
|
||||
assertEquals(new PatternsRequestCondition("/type1", "/type2"), c1.combine(c2));
|
||||
|
||||
c1 = new PatternsRequestCondition();
|
||||
c2 = new PatternsRequestCondition("/method1", "/method2");
|
||||
|
||||
assertEquals(new PatternsRequestCondition("/method1", "/method2"), c1.combine(c2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineMultiplePatterns() {
|
||||
PatternsRequestCondition c1 = new PatternsRequestCondition("/t1", "/t2");
|
||||
PatternsRequestCondition c2 = new PatternsRequestCondition("/m1", "/m2");
|
||||
|
||||
assertEquals(new PatternsRequestCondition("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"), c1.combine(c2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchDirectPath() throws Exception {
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo"));
|
||||
|
||||
assertNotNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchPattern() throws Exception {
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition("/foo/*");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo/bar"));
|
||||
|
||||
assertNotNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchSortPatterns() throws Exception {
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition("/**", "/foo/bar", "/foo/*");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo/bar"));
|
||||
PatternsRequestCondition expected = new PatternsRequestCondition("/foo/bar", "/foo/*", "/**");
|
||||
|
||||
assertEquals(expected, match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchSuffixPattern() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("/foo.html");
|
||||
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition("/{foo}");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
assertEquals("/{foo}.*", match.getPatterns().iterator().next());
|
||||
|
||||
condition = new PatternsRequestCondition(new String[] {"/{foo}"}, null, null, false, false, null);
|
||||
match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
assertEquals("/{foo}", match.getPatterns().iterator().next());
|
||||
}
|
||||
|
||||
// SPR-8410
|
||||
|
||||
@Test
|
||||
public void matchSuffixPatternUsingFileExtensions() throws Exception {
|
||||
String[] patterns = new String[] {"/jobs/{jobName}"};
|
||||
List<String> extensions = Collections.singletonList("json");
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition(patterns, null, null, true, false, extensions);
|
||||
|
||||
ServerWebExchange exchange = createExchange("/jobs/my.job");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
assertEquals("/jobs/{jobName}", match.getPatterns().iterator().next());
|
||||
|
||||
exchange = createExchange("/jobs/my.job.json");
|
||||
match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
assertEquals("/jobs/{jobName}.json", match.getPatterns().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchSuffixPatternUsingFileExtensions2() throws Exception {
|
||||
PatternsRequestCondition condition1 = new PatternsRequestCondition(
|
||||
new String[] {"/prefix"}, null, null, true, false, Collections.singletonList("json"));
|
||||
|
||||
PatternsRequestCondition condition2 = new PatternsRequestCondition(
|
||||
new String[] {"/suffix"}, null, null, true, false, null);
|
||||
|
||||
PatternsRequestCondition combined = condition1.combine(condition2);
|
||||
|
||||
ServerWebExchange exchange = createExchange("/prefix/suffix.json");
|
||||
PatternsRequestCondition match = combined.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchTrailingSlash() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("/foo/");
|
||||
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
assertEquals("Should match by default", "/foo/", match.getPatterns().iterator().next());
|
||||
|
||||
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, true, null);
|
||||
match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
assertEquals("Trailing slash should be insensitive to useSuffixPatternMatch settings (SPR-6164, SPR-5636)",
|
||||
"/foo/", match.getPatterns().iterator().next());
|
||||
|
||||
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, false, null);
|
||||
match = condition.getMatchingCondition(exchange);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchPatternContainsExtension() throws Exception {
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition("/foo.jpg");
|
||||
PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo.html"));
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareEqualPatterns() throws Exception {
|
||||
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo*");
|
||||
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo*");
|
||||
|
||||
assertEquals(0, c1.compareTo(c2, createExchange("/foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparePatternSpecificity() throws Exception {
|
||||
PatternsRequestCondition c1 = new PatternsRequestCondition("/fo*");
|
||||
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo");
|
||||
|
||||
assertEquals(1, c1.compareTo(c2, createExchange("/foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareNumberOfMatchingPatterns() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("/foo.html");
|
||||
|
||||
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo", "*.jpeg");
|
||||
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo", "*.html");
|
||||
|
||||
PatternsRequestCondition match1 = c1.getMatchingCondition(exchange);
|
||||
PatternsRequestCondition match2 = c2.getMatchingCondition(exchange);
|
||||
|
||||
assertNotNull(match1);
|
||||
assertEquals(1, match1.compareTo(match2, exchange));
|
||||
}
|
||||
|
||||
|
||||
private ServerWebExchange createExchange(String path) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI(path));
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ProducesRequestCondition}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ProducesRequestConditionTests {
|
||||
|
||||
@Test
|
||||
public void match() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("text/plain");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchNegated() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("!text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProducibleMediaTypes() throws Exception {
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("!application/xml");
|
||||
assertEquals(Collections.emptySet(), condition.getProducibleMediaTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchWildcard() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("text/*");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchMultiple() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("text/plain", "application/xml");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchSingle() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("application/xml");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchParseError() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("bogus");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchParseErrorWithNegation() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("bogus");
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("!text/plain");
|
||||
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo() throws Exception {
|
||||
ProducesRequestCondition html = new ProducesRequestCondition("text/html");
|
||||
ProducesRequestCondition xml = new ProducesRequestCondition("application/xml");
|
||||
ProducesRequestCondition none = new ProducesRequestCondition();
|
||||
|
||||
ServerWebExchange exchange = createExchange("application/xml, text/html");
|
||||
|
||||
assertTrue(html.compareTo(xml, exchange) > 0);
|
||||
assertTrue(xml.compareTo(html, exchange) < 0);
|
||||
assertTrue(xml.compareTo(none, exchange) < 0);
|
||||
assertTrue(none.compareTo(xml, exchange) > 0);
|
||||
assertTrue(html.compareTo(none, exchange) < 0);
|
||||
assertTrue(none.compareTo(html, exchange) > 0);
|
||||
|
||||
exchange = createExchange("application/xml, text/*");
|
||||
|
||||
assertTrue(html.compareTo(xml, exchange) > 0);
|
||||
assertTrue(xml.compareTo(html, exchange) < 0);
|
||||
|
||||
exchange = createExchange("application/pdf");
|
||||
|
||||
assertTrue(html.compareTo(xml, exchange) == 0);
|
||||
assertTrue(xml.compareTo(html, exchange) == 0);
|
||||
|
||||
// See SPR-7000
|
||||
exchange = createExchange("text/html;q=0.9,application/xml");
|
||||
|
||||
assertTrue(html.compareTo(xml, exchange) > 0);
|
||||
assertTrue(xml.compareTo(html, exchange) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareToWithSingleExpression() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("text/*");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareToMultipleExpressions() throws Exception {
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition("*/*", "text/plain");
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("text/*", "text/plain;q=0.7");
|
||||
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareToMultipleExpressionsAndMultipeAcceptHeaderValues() throws Exception {
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/*", "text/plain");
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/*", "application/xml");
|
||||
|
||||
ServerWebExchange exchange = createExchange("text/plain", "application/xml");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
|
||||
exchange = createExchange("application/xml", "text/plain");
|
||||
|
||||
result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
}
|
||||
|
||||
// SPR-8536
|
||||
|
||||
@Test
|
||||
public void compareToMediaTypeAll() throws Exception {
|
||||
ServerWebExchange exchange = createExchange();
|
||||
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition();
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/json");
|
||||
|
||||
assertTrue("Should have picked '*/*' condition as an exact match",
|
||||
condition1.compareTo(condition2, exchange) < 0);
|
||||
assertTrue("Should have picked '*/*' condition as an exact match",
|
||||
condition2.compareTo(condition1, exchange) > 0);
|
||||
|
||||
condition1 = new ProducesRequestCondition("*/*");
|
||||
condition2 = new ProducesRequestCondition("application/json");
|
||||
|
||||
assertTrue(condition1.compareTo(condition2, exchange) < 0);
|
||||
assertTrue(condition2.compareTo(condition1, exchange) > 0);
|
||||
|
||||
exchange = createExchange("*/*");
|
||||
|
||||
condition1 = new ProducesRequestCondition();
|
||||
condition2 = new ProducesRequestCondition("application/json");
|
||||
|
||||
assertTrue(condition1.compareTo(condition2, exchange) < 0);
|
||||
assertTrue(condition2.compareTo(condition1, exchange) > 0);
|
||||
|
||||
condition1 = new ProducesRequestCondition("*/*");
|
||||
condition2 = new ProducesRequestCondition("application/json");
|
||||
|
||||
assertTrue(condition1.compareTo(condition2, exchange) < 0);
|
||||
assertTrue(condition2.compareTo(condition1, exchange) > 0);
|
||||
}
|
||||
|
||||
// SPR-9021
|
||||
|
||||
@Test
|
||||
public void compareToMediaTypeAllWithParameter() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("*/*;q=0.9");
|
||||
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition();
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/json");
|
||||
|
||||
assertTrue(condition1.compareTo(condition2, exchange) < 0);
|
||||
assertTrue(condition2.compareTo(condition1, exchange) > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareToEqualMatch() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/*");
|
||||
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("text/xhtml");
|
||||
|
||||
int result = condition1.compareTo(condition2, exchange);
|
||||
assertTrue("Should have used MediaType.equals(Object) to break the match", result < 0);
|
||||
|
||||
result = condition2.compareTo(condition1, exchange);
|
||||
assertTrue("Should have used MediaType.equals(Object) to break the match", result > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combine() throws Exception {
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/xml");
|
||||
|
||||
ProducesRequestCondition result = condition1.combine(condition2);
|
||||
assertEquals(condition2, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineWithDefault() throws Exception {
|
||||
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||
ProducesRequestCondition condition2 = new ProducesRequestCondition();
|
||||
|
||||
ProducesRequestCondition result = condition1.combine(condition2);
|
||||
assertEquals(condition1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instantiateWithProducesAndHeaderConditions() throws Exception {
|
||||
String[] produces = new String[] {"text/plain"};
|
||||
String[] headers = new String[]{"foo=bar", "accept=application/xml,application/pdf"};
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition(produces, headers);
|
||||
|
||||
assertConditions(condition, "text/plain", "application/xml", "application/pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMatchingCondition() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("text/plain");
|
||||
|
||||
ProducesRequestCondition condition = new ProducesRequestCondition("text/plain", "application/xml");
|
||||
|
||||
ProducesRequestCondition result = condition.getMatchingCondition(exchange);
|
||||
assertConditions(result, "text/plain");
|
||||
|
||||
condition = new ProducesRequestCondition("application/xml");
|
||||
|
||||
result = condition.getMatchingCondition(exchange);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
private void assertConditions(ProducesRequestCondition condition, String... expected) {
|
||||
Collection<ProducesRequestCondition.ProduceMediaTypeExpression> expressions = condition.getContent();
|
||||
assertEquals("Invalid number of conditions", expressions.size(), expected.length);
|
||||
for (String s : expected) {
|
||||
boolean found = false;
|
||||
for (ProducesRequestCondition.ProduceMediaTypeExpression expr : expressions) {
|
||||
String conditionMediaType = expr.getMediaType().toString();
|
||||
if (conditionMediaType.equals(s)) {
|
||||
found = true;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fail("Condition [" + s + "] not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ServerWebExchange createExchange(String... accept) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||
if (accept != null) {
|
||||
for (String value : accept) {
|
||||
request.getHeaders().add("Accept", value);
|
||||
}
|
||||
}
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RequestConditionHolder}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestConditionHolderTests {
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.exchange = createExchange();
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange() throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void combine() {
|
||||
RequestConditionHolder params1 = new RequestConditionHolder(new ParamsRequestCondition("name1"));
|
||||
RequestConditionHolder params2 = new RequestConditionHolder(new ParamsRequestCondition("name2"));
|
||||
RequestConditionHolder expected = new RequestConditionHolder(new ParamsRequestCondition("name1", "name2"));
|
||||
|
||||
assertEquals(expected, params1.combine(params2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineEmpty() {
|
||||
RequestConditionHolder empty = new RequestConditionHolder(null);
|
||||
RequestConditionHolder notEmpty = new RequestConditionHolder(new ParamsRequestCondition("name"));
|
||||
|
||||
assertSame(empty, empty.combine(empty));
|
||||
assertSame(notEmpty, notEmpty.combine(empty));
|
||||
assertSame(notEmpty, empty.combine(notEmpty));
|
||||
}
|
||||
|
||||
@Test(expected=ClassCastException.class)
|
||||
public void combineIncompatible() {
|
||||
RequestConditionHolder params = new RequestConditionHolder(new ParamsRequestCondition("name"));
|
||||
RequestConditionHolder headers = new RequestConditionHolder(new HeadersRequestCondition("name"));
|
||||
params.combine(headers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void match() {
|
||||
RequestMethodsRequestCondition rm = new RequestMethodsRequestCondition(RequestMethod.GET, RequestMethod.POST);
|
||||
RequestConditionHolder custom = new RequestConditionHolder(rm);
|
||||
RequestMethodsRequestCondition expected = new RequestMethodsRequestCondition(RequestMethod.GET);
|
||||
|
||||
RequestConditionHolder holder = custom.getMatchingCondition(this.exchange);
|
||||
assertNotNull(holder);
|
||||
assertEquals(expected, holder.getCondition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMatch() {
|
||||
RequestMethodsRequestCondition rm = new RequestMethodsRequestCondition(RequestMethod.POST);
|
||||
RequestConditionHolder custom = new RequestConditionHolder(rm);
|
||||
|
||||
assertNull(custom.getMatchingCondition(this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchEmpty() {
|
||||
RequestConditionHolder empty = new RequestConditionHolder(null);
|
||||
assertSame(empty, empty.getMatchingCondition(this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare() {
|
||||
RequestConditionHolder params11 = new RequestConditionHolder(new ParamsRequestCondition("1"));
|
||||
RequestConditionHolder params12 = new RequestConditionHolder(new ParamsRequestCondition("1", "2"));
|
||||
|
||||
assertEquals(1, params11.compareTo(params12, this.exchange));
|
||||
assertEquals(-1, params12.compareTo(params11, this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareEmpty() {
|
||||
RequestConditionHolder empty = new RequestConditionHolder(null);
|
||||
RequestConditionHolder empty2 = new RequestConditionHolder(null);
|
||||
RequestConditionHolder notEmpty = new RequestConditionHolder(new ParamsRequestCondition("name"));
|
||||
|
||||
assertEquals(0, empty.compareTo(empty2, this.exchange));
|
||||
assertEquals(-1, notEmpty.compareTo(empty, this.exchange));
|
||||
assertEquals(1, empty.compareTo(notEmpty, this.exchange));
|
||||
}
|
||||
|
||||
@Test(expected=ClassCastException.class)
|
||||
public void compareIncompatible() {
|
||||
RequestConditionHolder params = new RequestConditionHolder(new ParamsRequestCondition("name"));
|
||||
RequestConditionHolder headers = new RequestConditionHolder(new HeadersRequestCondition("name"));
|
||||
params.compareTo(headers, this.exchange);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.reactive.result.method.RequestMappingInfo;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RequestMappingInfo}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMappingInfoTests {
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
private ServerHttpRequest request;
|
||||
|
||||
|
||||
// TODO: CORS pre-flight (see @Ignored)
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/foo"));
|
||||
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createEmpty() {
|
||||
RequestMappingInfo info = new RequestMappingInfo(null, null, null, null, null, null, null);
|
||||
|
||||
assertEquals(0, info.getPatternsCondition().getPatterns().size());
|
||||
assertEquals(0, info.getMethodsCondition().getMethods().size());
|
||||
assertEquals(true, info.getConsumesCondition().isEmpty());
|
||||
assertEquals(true, info.getProducesCondition().isEmpty());
|
||||
assertNotNull(info.getParamsCondition());
|
||||
assertNotNull(info.getHeadersCondition());
|
||||
assertNull(info.getCustomCondition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchPatternsCondition() {
|
||||
RequestMappingInfo info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo*", "/bar"), null, null, null, null, null, null);
|
||||
RequestMappingInfo expected = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo*"), null, null, null, null, null, null);
|
||||
|
||||
assertEquals(expected, info.getMatchingCondition(this.exchange));
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/**", "/foo*", "/foo"), null, null, null, null, null, null);
|
||||
expected = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo", "/foo*", "/**"), null, null, null, null, null, null);
|
||||
|
||||
assertEquals(expected, info.getMatchingCondition(this.exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchParamsCondition() {
|
||||
this.request.getQueryParams().add("foo", "bar");
|
||||
|
||||
RequestMappingInfo info =
|
||||
new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null,
|
||||
new ParamsRequestCondition("foo=bar"), null, null, null, null);
|
||||
RequestMappingInfo match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null,
|
||||
new ParamsRequestCondition("foo!=bar"), null, null, null, null);
|
||||
match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchHeadersCondition() {
|
||||
this.request.getHeaders().add("foo", "bar");
|
||||
|
||||
RequestMappingInfo info =
|
||||
new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null,
|
||||
new HeadersRequestCondition("foo=bar"), null, null, null);
|
||||
RequestMappingInfo match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null,
|
||||
new HeadersRequestCondition("foo!=bar"), null, null, null);
|
||||
match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchConsumesCondition() {
|
||||
this.request.getHeaders().setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
RequestMappingInfo info =
|
||||
new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null, null,
|
||||
new ConsumesRequestCondition("text/plain"), null, null);
|
||||
RequestMappingInfo match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null, null,
|
||||
new ConsumesRequestCondition("application/xml"), null, null);
|
||||
match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchProducesCondition() {
|
||||
this.request.getHeaders().setAccept(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
|
||||
RequestMappingInfo info =
|
||||
new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null, null, null,
|
||||
new ProducesRequestCondition("text/plain"), null);
|
||||
RequestMappingInfo match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null, null, null,
|
||||
new ProducesRequestCondition("application/xml"), null);
|
||||
match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchCustomCondition() {
|
||||
this.request.getQueryParams().add("foo", "bar");
|
||||
|
||||
RequestMappingInfo info =
|
||||
new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null, null, null, null, null,
|
||||
new ParamsRequestCondition("foo=bar"));
|
||||
RequestMappingInfo match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), null,
|
||||
new ParamsRequestCondition("foo!=bar"), null, null, null,
|
||||
new ParamsRequestCondition("foo!=bar"));
|
||||
match = info.getMatchingCondition(this.exchange);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTwoHttpMethodsOneParam() {
|
||||
RequestMappingInfo none = new RequestMappingInfo(null, null, null, null, null, null, null);
|
||||
RequestMappingInfo oneMethod =
|
||||
new RequestMappingInfo(null,
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET), null, null, null, null, null);
|
||||
RequestMappingInfo oneMethodOneParam =
|
||||
new RequestMappingInfo(null,
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo"), null, null, null, null);
|
||||
|
||||
Comparator<RequestMappingInfo> comparator = (info, otherInfo) -> info.compareTo(otherInfo, exchange);
|
||||
|
||||
List<RequestMappingInfo> list = asList(none, oneMethod, oneMethodOneParam);
|
||||
Collections.shuffle(list);
|
||||
Collections.sort(list, comparator);
|
||||
|
||||
assertEquals(oneMethodOneParam, list.get(0));
|
||||
assertEquals(oneMethod, list.get(1));
|
||||
assertEquals(none, list.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals() {
|
||||
RequestMappingInfo info1 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
RequestMappingInfo info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertEquals(info1, info2);
|
||||
assertEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo", "/NOOOOOO"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET, RequestMethod.POST),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("/NOOOOOO"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("/NOOOOOO"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/NOOOOOO"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/NOOOOOO"),
|
||||
new ParamsRequestCondition("customFoo=customBar"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
|
||||
info2 = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"),
|
||||
new RequestMethodsRequestCondition(RequestMethod.GET),
|
||||
new ParamsRequestCondition("foo=bar"),
|
||||
new HeadersRequestCondition("foo=bar"),
|
||||
new ConsumesRequestCondition("text/plain"),
|
||||
new ProducesRequestCondition("text/plain"),
|
||||
new ParamsRequestCondition("customFoo=NOOOOOO"));
|
||||
|
||||
assertFalse(info1.equals(info2));
|
||||
assertNotEquals(info1.hashCode(), info2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void preFlightRequest() throws Exception {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.OPTIONS, new URI("/foo"));
|
||||
request.getHeaders().add(HttpHeaders.ORIGIN, "http://domain.com");
|
||||
request.getHeaders().add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST");
|
||||
|
||||
WebSessionManager manager = mock(WebSessionManager.class);
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange = new DefaultServerWebExchange(request, response, manager);
|
||||
|
||||
RequestMappingInfo info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), new RequestMethodsRequestCondition(RequestMethod.POST), null,
|
||||
null, null, null, null);
|
||||
RequestMappingInfo match = info.getMatchingCondition(exchange);
|
||||
assertNotNull(match);
|
||||
|
||||
info = new RequestMappingInfo(
|
||||
new PatternsRequestCondition("/foo"), new RequestMethodsRequestCondition(RequestMethod.OPTIONS), null,
|
||||
null, null, null, null);
|
||||
match = info.getMatchingCondition(exchange);
|
||||
assertNull("Pre-flight should match the ACCESS_CONTROL_REQUEST_METHOD", match);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.reactive.result.condition;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.HEAD;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.OPTIONS;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RequestMethodsRequestCondition}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMethodsRequestConditionTests {
|
||||
|
||||
// TODO: custom method, CORS pre-flight (see @Ignored)
|
||||
|
||||
@Test
|
||||
public void getMatchingCondition() throws Exception {
|
||||
testMatch(new RequestMethodsRequestCondition(GET), GET);
|
||||
testMatch(new RequestMethodsRequestCondition(GET, POST), GET);
|
||||
testNoMatch(new RequestMethodsRequestCondition(GET), POST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMatchingConditionWithHttpHead() throws Exception {
|
||||
testMatch(new RequestMethodsRequestCondition(HEAD), HEAD);
|
||||
testMatch(new RequestMethodsRequestCondition(GET), HEAD);
|
||||
testNoMatch(new RequestMethodsRequestCondition(POST), HEAD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMatchingConditionWithEmptyConditions() throws Exception {
|
||||
RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();
|
||||
for (RequestMethod method : RequestMethod.values()) {
|
||||
if (!OPTIONS.equals(method)) {
|
||||
ServerWebExchange exchange = createExchange(method.name());
|
||||
assertNotNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
}
|
||||
testNoMatch(condition, OPTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getMatchingConditionWithCustomMethod() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("PROPFIND");
|
||||
assertNotNull(new RequestMethodsRequestCondition().getMatchingCondition(exchange));
|
||||
assertNull(new RequestMethodsRequestCondition(GET, POST).getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getMatchingConditionWithCorsPreFlight() throws Exception {
|
||||
ServerWebExchange exchange = createExchange("OPTIONS");
|
||||
exchange.getRequest().getHeaders().add("Origin", "http://example.com");
|
||||
exchange.getRequest().getHeaders().add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT");
|
||||
|
||||
assertNotNull(new RequestMethodsRequestCondition().getMatchingCondition(exchange));
|
||||
assertNotNull(new RequestMethodsRequestCondition(PUT).getMatchingCondition(exchange));
|
||||
assertNull(new RequestMethodsRequestCondition(DELETE).getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo() throws Exception {
|
||||
RequestMethodsRequestCondition c1 = new RequestMethodsRequestCondition(GET, HEAD);
|
||||
RequestMethodsRequestCondition c2 = new RequestMethodsRequestCondition(POST);
|
||||
RequestMethodsRequestCondition c3 = new RequestMethodsRequestCondition();
|
||||
|
||||
ServerWebExchange exchange = createExchange();
|
||||
|
||||
int result = c1.compareTo(c2, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = c2.compareTo(c1, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||
|
||||
result = c2.compareTo(c3, exchange);
|
||||
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||
|
||||
result = c1.compareTo(c1, exchange);
|
||||
assertEquals("Invalid comparison result ", 0, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combine() {
|
||||
RequestMethodsRequestCondition condition1 = new RequestMethodsRequestCondition(GET);
|
||||
RequestMethodsRequestCondition condition2 = new RequestMethodsRequestCondition(POST);
|
||||
|
||||
RequestMethodsRequestCondition result = condition1.combine(condition2);
|
||||
assertEquals(2, result.getContent().size());
|
||||
}
|
||||
|
||||
|
||||
private void testMatch(RequestMethodsRequestCondition condition, RequestMethod method) throws Exception {
|
||||
ServerWebExchange exchange = createExchange(method.name());
|
||||
RequestMethodsRequestCondition actual = condition.getMatchingCondition(exchange);
|
||||
assertNotNull(actual);
|
||||
assertEquals(Collections.singleton(method), actual.getContent());
|
||||
}
|
||||
|
||||
private void testNoMatch(RequestMethodsRequestCondition condition, RequestMethod method) throws Exception {
|
||||
ServerWebExchange exchange = createExchange(method.name());
|
||||
assertNull(condition.getMatchingCondition(exchange));
|
||||
}
|
||||
|
||||
|
||||
private ServerWebExchange createExchange() throws URISyntaxException {
|
||||
return createExchange(null);
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange(String method) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.resolve(method), new URI("/"));
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user