SPR-7353 - Added equivalent of JAX-RS @Produces to Spring MVC
This commit is contained in:
parent
a557878a6f
commit
bb2cc8457f
|
|
@ -25,7 +25,6 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
|
@ -141,7 +140,9 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
||||||
RequestConditionFactory.parseMethods(annotation.method()),
|
RequestConditionFactory.parseMethods(annotation.method()),
|
||||||
RequestConditionFactory.parseParams(annotation.params()),
|
RequestConditionFactory.parseParams(annotation.params()),
|
||||||
RequestConditionFactory.parseHeaders(annotation.headers()),
|
RequestConditionFactory.parseHeaders(annotation.headers()),
|
||||||
RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()));
|
RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()),
|
||||||
|
RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -257,13 +258,10 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
/*
|
result = mapping.getProduces().compareTo(otherMapping.getProduces(), this.requestAcceptHeader);
|
||||||
TODO: fix
|
|
||||||
result = compareAcceptHeaders(mapping.getAcceptHeaderMediaTypes(), otherMapping.getAcceptHeaderMediaTypes());
|
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
result = mapping.getMethods().compareTo(otherMapping.getMethods());
|
result = mapping.getMethods().compareTo(otherMapping.getMethods());
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
|
import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
|
||||||
import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
|
import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
|
||||||
import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
|
import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
|
||||||
|
import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition;
|
||||||
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
|
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
|
||||||
import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
|
import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
|
||||||
|
|
||||||
|
|
@ -57,6 +58,8 @@ public final class RequestMappingInfo {
|
||||||
|
|
||||||
private final ConsumesRequestCondition consumesCondition;
|
private final ConsumesRequestCondition consumesCondition;
|
||||||
|
|
||||||
|
private final ProducesRequestCondition producesCondition;
|
||||||
|
|
||||||
private int hash;
|
private int hash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,7 +68,7 @@ public final class RequestMappingInfo {
|
||||||
* <p>Package protected for testing purposes.
|
* <p>Package protected for testing purposes.
|
||||||
*/
|
*/
|
||||||
RequestMappingInfo(Collection<String> patterns, RequestMethod[] methods) {
|
RequestMappingInfo(Collection<String> patterns, RequestMethod[] methods) {
|
||||||
this(patterns, RequestConditionFactory.parseMethods(methods), null, null, null);
|
this(patterns, RequestConditionFactory.parseMethods(methods), null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,12 +78,14 @@ public final class RequestMappingInfo {
|
||||||
RequestMethodsRequestCondition methodsCondition,
|
RequestMethodsRequestCondition methodsCondition,
|
||||||
ParamsRequestCondition paramsCondition,
|
ParamsRequestCondition paramsCondition,
|
||||||
HeadersRequestCondition headersCondition,
|
HeadersRequestCondition headersCondition,
|
||||||
ConsumesRequestCondition consumesCondition) {
|
ConsumesRequestCondition consumesCondition,
|
||||||
|
ProducesRequestCondition producesCondition) {
|
||||||
this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns));
|
this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns));
|
||||||
this.methodsCondition = methodsCondition != null ? methodsCondition : new RequestMethodsRequestCondition();
|
this.methodsCondition = methodsCondition != null ? methodsCondition : new RequestMethodsRequestCondition();
|
||||||
this.paramsCondition = paramsCondition != null ? paramsCondition : new ParamsRequestCondition();
|
this.paramsCondition = paramsCondition != null ? paramsCondition : new ParamsRequestCondition();
|
||||||
this.headersCondition = headersCondition != null ? headersCondition : new HeadersRequestCondition();
|
this.headersCondition = headersCondition != null ? headersCondition : new HeadersRequestCondition();
|
||||||
this.consumesCondition = consumesCondition != null ? consumesCondition : new ConsumesRequestCondition();
|
this.consumesCondition = consumesCondition != null ? consumesCondition : new ConsumesRequestCondition();
|
||||||
|
this.producesCondition = producesCondition != null ? producesCondition : new ProducesRequestCondition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
|
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
|
||||||
|
|
@ -106,40 +111,47 @@ public final class RequestMappingInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the patterns of this request key.
|
* Returns the patterns of this request mapping info.
|
||||||
*/
|
*/
|
||||||
public Set<String> getPatterns() {
|
public Set<String> getPatterns() {
|
||||||
return patterns;
|
return patterns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the request method conditions of this request key.
|
* Returns the request method conditions of this request mapping info.
|
||||||
*/
|
*/
|
||||||
public RequestMethodsRequestCondition getMethods() {
|
public RequestMethodsRequestCondition getMethods() {
|
||||||
return methodsCondition;
|
return methodsCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the request parameters conditions of this request key.
|
* Returns the request parameters conditions of this request mapping info.
|
||||||
*/
|
*/
|
||||||
public ParamsRequestCondition getParams() {
|
public ParamsRequestCondition getParams() {
|
||||||
return paramsCondition;
|
return paramsCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the request headers conditions of this request key.
|
* Returns the request headers conditions of this request mapping info.
|
||||||
*/
|
*/
|
||||||
public HeadersRequestCondition getHeaders() {
|
public HeadersRequestCondition getHeaders() {
|
||||||
return headersCondition;
|
return headersCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the request consumes conditions of this request key.
|
* Returns the request consumes conditions of this request mapping info.
|
||||||
*/
|
*/
|
||||||
public ConsumesRequestCondition getConsumes() {
|
public ConsumesRequestCondition getConsumes() {
|
||||||
return consumesCondition;
|
return consumesCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request produces conditions of this request mapping info.
|
||||||
|
*/
|
||||||
|
public ProducesRequestCondition getProduces() {
|
||||||
|
return producesCondition;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines this {@code RequestMappingInfo} with another as follows:
|
* Combines this {@code RequestMappingInfo} with another as follows:
|
||||||
* <ul>
|
* <ul>
|
||||||
|
|
@ -156,7 +168,7 @@ public final class RequestMappingInfo {
|
||||||
* </ul>
|
* </ul>
|
||||||
* @param methodKey the key to combine with
|
* @param methodKey the key to combine with
|
||||||
* @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns
|
* @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns
|
||||||
* @return a new request key containing conditions from both keys
|
* @return a new request mapping info containing conditions from both keys
|
||||||
*/
|
*/
|
||||||
public RequestMappingInfo combine(RequestMappingInfo methodKey, PathMatcher pathMatcher) {
|
public RequestMappingInfo combine(RequestMappingInfo methodKey, PathMatcher pathMatcher) {
|
||||||
Set<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
|
Set<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
|
||||||
|
|
@ -164,8 +176,9 @@ public final class RequestMappingInfo {
|
||||||
ParamsRequestCondition params = this.paramsCondition.combine(methodKey.paramsCondition);
|
ParamsRequestCondition params = this.paramsCondition.combine(methodKey.paramsCondition);
|
||||||
HeadersRequestCondition headers = this.headersCondition.combine(methodKey.headersCondition);
|
HeadersRequestCondition headers = this.headersCondition.combine(methodKey.headersCondition);
|
||||||
ConsumesRequestCondition consumes = this.consumesCondition.combine(methodKey.consumesCondition);
|
ConsumesRequestCondition consumes = this.consumesCondition.combine(methodKey.consumesCondition);
|
||||||
|
ProducesRequestCondition produces = this.producesCondition.combine(methodKey.producesCondition);
|
||||||
|
|
||||||
return new RequestMappingInfo(patterns, methods, params, headers, consumes);
|
return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> combinePatterns(Collection<String> typePatterns,
|
private static Set<String> combinePatterns(Collection<String> typePatterns,
|
||||||
|
|
@ -203,23 +216,24 @@ public final class RequestMappingInfo {
|
||||||
* @param lookupPath mapping lookup path within the current servlet mapping if applicable
|
* @param lookupPath mapping lookup path within the current servlet mapping if applicable
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param pathMatcher to check for matching patterns
|
* @param pathMatcher to check for matching patterns
|
||||||
* @return a new request key that contains all matching attributes, or {@code null} if not all conditions match
|
* @return a new request mapping info that contains all matching attributes, or {@code null} if not all conditions match
|
||||||
*/
|
*/
|
||||||
public RequestMappingInfo getMatchingRequestMapping(String lookupPath, HttpServletRequest request, PathMatcher pathMatcher) {
|
public RequestMappingInfo getMatchingRequestMapping(String lookupPath, HttpServletRequest request, PathMatcher pathMatcher) {
|
||||||
RequestMethodsRequestCondition matchingMethodCondition = methodsCondition.getMatchingCondition(request);
|
RequestMethodsRequestCondition matchingMethodCondition = methodsCondition.getMatchingCondition(request);
|
||||||
ParamsRequestCondition matchingParamsCondition = paramsCondition.getMatchingCondition(request);
|
ParamsRequestCondition matchingParamsCondition = paramsCondition.getMatchingCondition(request);
|
||||||
HeadersRequestCondition matchingHeadersCondition = headersCondition.getMatchingCondition(request);
|
HeadersRequestCondition matchingHeadersCondition = headersCondition.getMatchingCondition(request);
|
||||||
ConsumesRequestCondition matchingConsumesCondition = consumesCondition.getMatchingCondition(request);
|
ConsumesRequestCondition matchingConsumesCondition = consumesCondition.getMatchingCondition(request);
|
||||||
|
ProducesRequestCondition matchingProducesCondition = producesCondition.getMatchingCondition(request);
|
||||||
|
|
||||||
if (matchingMethodCondition == null || matchingParamsCondition == null || matchingHeadersCondition == null ||
|
if (matchingMethodCondition == null || matchingParamsCondition == null || matchingHeadersCondition == null ||
|
||||||
matchingConsumesCondition == null) {
|
matchingConsumesCondition == null || matchingProducesCondition == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
List<String> matchingPatterns = getMatchingPatterns(lookupPath, pathMatcher);
|
List<String> matchingPatterns = getMatchingPatterns(lookupPath, pathMatcher);
|
||||||
if (!matchingPatterns.isEmpty()) {
|
if (!matchingPatterns.isEmpty()) {
|
||||||
return new RequestMappingInfo(matchingPatterns, matchingMethodCondition, matchingParamsCondition,
|
return new RequestMappingInfo(matchingPatterns, matchingMethodCondition, matchingParamsCondition,
|
||||||
matchingHeadersCondition, matchingConsumesCondition);
|
matchingHeadersCondition, matchingConsumesCondition, matchingProducesCondition);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -271,7 +285,8 @@ public final class RequestMappingInfo {
|
||||||
this.methodsCondition.equals(other.methodsCondition) &&
|
this.methodsCondition.equals(other.methodsCondition) &&
|
||||||
this.paramsCondition.equals(other.paramsCondition) &&
|
this.paramsCondition.equals(other.paramsCondition) &&
|
||||||
this.headersCondition.equals(other.headersCondition) &&
|
this.headersCondition.equals(other.headersCondition) &&
|
||||||
this.consumesCondition.equals(other.consumesCondition));
|
this.consumesCondition.equals(other.consumesCondition) &&
|
||||||
|
this.producesCondition.equals(other.producesCondition));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +300,7 @@ public final class RequestMappingInfo {
|
||||||
result = 31 * result + paramsCondition.hashCode();
|
result = 31 * result + paramsCondition.hashCode();
|
||||||
result = 31 * result + headersCondition.hashCode();
|
result = 31 * result + headersCondition.hashCode();
|
||||||
result = 31 * result + consumesCondition.hashCode();
|
result = 31 * result + consumesCondition.hashCode();
|
||||||
|
result = 31 * result + producesCondition.hashCode();
|
||||||
hash = result;
|
hash = result;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -298,6 +314,7 @@ public final class RequestMappingInfo {
|
||||||
builder.append(",params=").append(paramsCondition);
|
builder.append(",params=").append(paramsCondition);
|
||||||
builder.append(",headers=").append(headersCondition);
|
builder.append(",headers=").append(headersCondition);
|
||||||
builder.append(",consumes=").append(consumesCondition);
|
builder.append(",consumes=").append(consumesCondition);
|
||||||
|
builder.append(",produces=").append(producesCondition);
|
||||||
builder.append('}');
|
builder.append('}');
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.mvc.method.condition;
|
package org.springframework.web.servlet.mvc.method.condition;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -27,33 +26,23 @@ import java.util.Set;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a collection of consumes conditions, typically obtained from {@link
|
* Represents a collection of consumes conditions, typically obtained from {@link org.springframework.web.bind.annotation.RequestMapping#consumes()
|
||||||
* org.springframework.web.bind.annotation.RequestMapping#consumes() @RequestMapping.consumes()}.
|
* @RequestMapping.consumes()}.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @see RequestConditionFactory#parseHeaders(String...)
|
* @see RequestConditionFactory#parseConsumes(String...)
|
||||||
|
* @see RequestConditionFactory#parseConsumes(String[], String[])
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public class ConsumesRequestCondition
|
public class ConsumesRequestCondition
|
||||||
extends LogicalDisjunctionRequestCondition<ConsumesRequestCondition.ConsumeRequestCondition>
|
extends MediaTypesRequestCondition<ConsumesRequestCondition.ConsumeRequestCondition>
|
||||||
implements Comparable<ConsumesRequestCondition> {
|
implements Comparable<ConsumesRequestCondition> {
|
||||||
|
|
||||||
private final ConsumeRequestCondition mostSpecificCondition;
|
|
||||||
|
|
||||||
ConsumesRequestCondition(Collection<ConsumeRequestCondition> conditions) {
|
ConsumesRequestCondition(Collection<ConsumeRequestCondition> conditions) {
|
||||||
super(conditions);
|
super(conditions);
|
||||||
Assert.notEmpty(conditions, "'conditions' must not be empty");
|
|
||||||
mostSpecificCondition = getMostSpecificCondition();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConsumeRequestCondition getMostSpecificCondition() {
|
|
||||||
List<ConsumeRequestCondition> conditions = new ArrayList<ConsumeRequestCondition>(getConditions());
|
|
||||||
Collections.sort(conditions);
|
|
||||||
return conditions.get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsumesRequestCondition(String... consumes) {
|
ConsumesRequestCondition(String... consumes) {
|
||||||
|
|
@ -72,7 +61,7 @@ public class ConsumesRequestCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an default set of consumes request conditions.
|
* Creates a default set of consumes request conditions.
|
||||||
*/
|
*/
|
||||||
public ConsumesRequestCondition() {
|
public ConsumesRequestCondition() {
|
||||||
this(Collections.singleton(new ConsumeRequestCondition(MediaType.ALL, false)));
|
this(Collections.singleton(new ConsumeRequestCondition(MediaType.ALL, false)));
|
||||||
|
|
@ -101,8 +90,8 @@ public class ConsumesRequestCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines this collection of request condition with another. Returns {@code other}, unless it has the default
|
* Combines this collection of request condition with another. Returns {@code other}, unless it has the default value
|
||||||
* value (i.e. <code>*/*</code>).
|
* (i.e. <code>*/*</code>).
|
||||||
*
|
*
|
||||||
* @param other the condition to combine with
|
* @param other the condition to combine with
|
||||||
*/
|
*/
|
||||||
|
|
@ -122,79 +111,32 @@ public class ConsumesRequestCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
public int compareTo(ConsumesRequestCondition other) {
|
public int compareTo(ConsumesRequestCondition other) {
|
||||||
return this.mostSpecificCondition.compareTo(other.mostSpecificCondition);
|
return this.getMostSpecificCondition().compareTo(other.getMostSpecificCondition());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaType getContentType(HttpServletRequest request) {
|
static class ConsumeRequestCondition extends MediaTypesRequestCondition.MediaTypeRequestCondition {
|
||||||
if (StringUtils.hasLength(request.getContentType())) {
|
|
||||||
return MediaType.parseMediaType(request.getContentType());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return MediaType.APPLICATION_OCTET_STREAM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ConsumeRequestCondition implements RequestCondition, Comparable<ConsumeRequestCondition> {
|
|
||||||
|
|
||||||
private final MediaType mediaType;
|
|
||||||
|
|
||||||
private final boolean isNegated;
|
|
||||||
|
|
||||||
ConsumeRequestCondition(String expression) {
|
ConsumeRequestCondition(String expression) {
|
||||||
if (expression.startsWith("!")) {
|
super(expression);
|
||||||
isNegated = true;
|
}
|
||||||
expression = expression.substring(1);
|
|
||||||
|
ConsumeRequestCondition(MediaType mediaType, boolean negated) {
|
||||||
|
super(mediaType, negated);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean match(HttpServletRequest request, MediaType mediaType) {
|
||||||
|
MediaType contentType = getContentType(request);
|
||||||
|
return mediaType.includes(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaType getContentType(HttpServletRequest request) {
|
||||||
|
if (StringUtils.hasLength(request.getContentType())) {
|
||||||
|
return MediaType.parseMediaType(request.getContentType());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
isNegated = false;
|
return MediaType.APPLICATION_OCTET_STREAM;
|
||||||
}
|
}
|
||||||
this.mediaType = MediaType.parseMediaType(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsumeRequestCondition(MediaType mediaType, boolean isNegated) {
|
|
||||||
this.mediaType = mediaType;
|
|
||||||
this.isNegated = isNegated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean match(HttpServletRequest request) {
|
|
||||||
MediaType contentType = getContentType(request);
|
|
||||||
boolean match = this.mediaType.includes(contentType);
|
|
||||||
return !isNegated ? match : !match;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int compareTo(ConsumeRequestCondition other) {
|
|
||||||
return MediaType.SPECIFICITY_COMPARATOR.compare(this.mediaType, other.mediaType);
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaType getMediaType() {
|
|
||||||
return mediaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj != null && obj instanceof ConsumeRequestCondition) {
|
|
||||||
ConsumeRequestCondition other = (ConsumeRequestCondition) obj;
|
|
||||||
return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return mediaType.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
if (isNegated) {
|
|
||||||
builder.append('!');
|
|
||||||
}
|
|
||||||
builder.append(mediaType.toString());
|
|
||||||
return builder.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.servlet.mvc.method.condition;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
*/
|
||||||
|
class MediaTypesRequestCondition<T extends MediaTypesRequestCondition.MediaTypeRequestCondition>
|
||||||
|
extends LogicalDisjunctionRequestCondition<T> {
|
||||||
|
|
||||||
|
private final List<T> sortedConditions;
|
||||||
|
|
||||||
|
public MediaTypesRequestCondition(Collection<T> conditions) {
|
||||||
|
super(conditions);
|
||||||
|
Assert.notEmpty(conditions, "'conditions' must not be empty");
|
||||||
|
sortedConditions = new ArrayList<T>(conditions);
|
||||||
|
Collections.sort(sortedConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaTypeRequestCondition getMostSpecificCondition(Collection<T> conditions) {
|
||||||
|
List<MediaTypeRequestCondition> conditionList = new ArrayList<MediaTypeRequestCondition>(conditions);
|
||||||
|
Collections.sort(conditionList);
|
||||||
|
return conditionList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MediaTypeRequestCondition getMostSpecificCondition() {
|
||||||
|
return sortedConditions.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<T> getSortedConditions() {
|
||||||
|
return sortedConditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
*/
|
||||||
|
protected abstract static class MediaTypeRequestCondition
|
||||||
|
implements RequestCondition, Comparable<MediaTypeRequestCondition> {
|
||||||
|
|
||||||
|
private final MediaType mediaType;
|
||||||
|
|
||||||
|
private final boolean isNegated;
|
||||||
|
|
||||||
|
MediaTypeRequestCondition(MediaType mediaType, boolean negated) {
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
isNegated = negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaTypeRequestCondition(String expression) {
|
||||||
|
if (expression.startsWith("!")) {
|
||||||
|
isNegated = true;
|
||||||
|
expression = expression.substring(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isNegated = false;
|
||||||
|
}
|
||||||
|
this.mediaType = MediaType.parseMediaType(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean match(HttpServletRequest request) {
|
||||||
|
boolean match = match(request, this.mediaType);
|
||||||
|
return !isNegated ? match : !match;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean match(HttpServletRequest request, MediaType mediaType);
|
||||||
|
|
||||||
|
MediaType getMediaType() {
|
||||||
|
return mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(MediaTypeRequestCondition 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().equals(obj.getClass())) {
|
||||||
|
MediaTypeRequestCondition other = (MediaTypeRequestCondition) obj;
|
||||||
|
return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return mediaType.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
if (isNegated) {
|
||||||
|
builder.append('!');
|
||||||
|
}
|
||||||
|
builder.append(mediaType.toString());
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.servlet.mvc.method.condition;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a collection of produces conditions, typically obtained from {@link
|
||||||
|
* org.springframework.web.bind.annotation.RequestMapping#produces() @RequestMapping.produces()}.
|
||||||
|
*
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
* @see RequestConditionFactory#parseProduces(String...)
|
||||||
|
* @see RequestConditionFactory#parseProduces(String[], String[])
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
public class ProducesRequestCondition
|
||||||
|
extends MediaTypesRequestCondition<ProducesRequestCondition.ProduceRequestCondition> {
|
||||||
|
|
||||||
|
ProducesRequestCondition(Collection<ProduceRequestCondition> conditions) {
|
||||||
|
super(conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ProducesRequestCondition(String... consumes) {
|
||||||
|
this(parseConditions(Arrays.asList(consumes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<ProduceRequestCondition> parseConditions(List<String> consumes) {
|
||||||
|
if (consumes.isEmpty()) {
|
||||||
|
consumes = Collections.singletonList("*/*");
|
||||||
|
}
|
||||||
|
Set<ProduceRequestCondition> conditions = new LinkedHashSet<ProduceRequestCondition>(consumes.size());
|
||||||
|
for (String consume : consumes) {
|
||||||
|
conditions.add(new ProduceRequestCondition(consume));
|
||||||
|
}
|
||||||
|
return conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an default set of consumes request conditions.
|
||||||
|
*/
|
||||||
|
public ProducesRequestCondition() {
|
||||||
|
this(Collections.singleton(new ProduceRequestCondition(MediaType.ALL, false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@code RequestCondition} that contains all conditions of this key that match the request.
|
||||||
|
*
|
||||||
|
* @param request the request
|
||||||
|
* @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match
|
||||||
|
*/
|
||||||
|
public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) {
|
||||||
|
Set<ProduceRequestCondition> matchingConditions = new LinkedHashSet<ProduceRequestCondition>(getConditions());
|
||||||
|
for (Iterator<ProduceRequestCondition> iterator = matchingConditions.iterator(); iterator.hasNext();) {
|
||||||
|
ProduceRequestCondition condition = iterator.next();
|
||||||
|
if (!condition.match(request)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchingConditions.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new ProducesRequestCondition(matchingConditions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines this collection of request condition with another. Returns {@code other}, unless it has the default
|
||||||
|
* value (i.e. {@code */*}).
|
||||||
|
*
|
||||||
|
* @param other the condition to combine with
|
||||||
|
*/
|
||||||
|
public ProducesRequestCondition combine(ProducesRequestCondition other) {
|
||||||
|
return !other.hasDefaultValue() ? other : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasDefaultValue() {
|
||||||
|
Set<ProduceRequestCondition> conditions = getConditions();
|
||||||
|
if (conditions.size() == 1) {
|
||||||
|
ProduceRequestCondition condition = conditions.iterator().next();
|
||||||
|
return condition.getMediaType().equals(MediaType.ALL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(ProducesRequestCondition other, List<MediaType> acceptedMediaTypes) {
|
||||||
|
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||||
|
int thisIndex = this.indexOfMediaType(acceptedMediaType);
|
||||||
|
int otherIndex = other.indexOfMediaType(acceptedMediaType);
|
||||||
|
if (thisIndex != otherIndex) {
|
||||||
|
return otherIndex - thisIndex;
|
||||||
|
} else if (thisIndex != -1 && otherIndex != -1) {
|
||||||
|
ProduceRequestCondition thisCondition = this.getSortedConditions().get(thisIndex);
|
||||||
|
ProduceRequestCondition otherCondition = other.getSortedConditions().get(otherIndex);
|
||||||
|
int result = thisCondition.compareTo(otherCondition);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOfMediaType(MediaType mediaType) {
|
||||||
|
List<ProduceRequestCondition> sortedConditions = getSortedConditions();
|
||||||
|
for (int i = 0; i < sortedConditions.size(); i++) {
|
||||||
|
ProduceRequestCondition condition = sortedConditions.get(i);
|
||||||
|
if (mediaType.includes(condition.getMediaType())) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ProduceRequestCondition extends MediaTypesRequestCondition.MediaTypeRequestCondition {
|
||||||
|
|
||||||
|
ProduceRequestCondition(MediaType mediaType, boolean negated) {
|
||||||
|
super(mediaType, negated);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProduceRequestCondition(String expression) {
|
||||||
|
super(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean match(HttpServletRequest request, MediaType mediaType) {
|
||||||
|
List<MediaType> acceptedMediaTypes = getAccept(request);
|
||||||
|
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||||
|
if (mediaType.isCompatibleWith(acceptedMediaType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MediaType> getAccept(HttpServletRequest request) {
|
||||||
|
String acceptHeader = request.getHeader("Accept");
|
||||||
|
if (StringUtils.hasLength(acceptHeader)) {
|
||||||
|
return MediaType.parseMediaTypes(acceptHeader);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Collections.singletonList(MediaType.ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -99,6 +99,15 @@ public abstract class RequestConditionFactory {
|
||||||
return new ConsumesRequestCondition(consumes);
|
return new ConsumesRequestCondition(consumes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given consumes and {@code Content-Type} headers, and returns them as a single request condition. <p>Only
|
||||||
|
* {@code Content-Type} headers will be used, all other headers will be ignored.
|
||||||
|
*
|
||||||
|
* @param consumes the consumes
|
||||||
|
* @param headers the headers
|
||||||
|
* @return the request condition
|
||||||
|
* @see org.springframework.web.bind.annotation.RequestMapping#consumes()
|
||||||
|
*/
|
||||||
public static ConsumesRequestCondition parseConsumes(String[] consumes, String[] headers) {
|
public static ConsumesRequestCondition parseConsumes(String[] consumes, String[] headers) {
|
||||||
|
|
||||||
List<ConsumesRequestCondition.ConsumeRequestCondition> allConditions = parseContentTypeHeaders(headers);
|
List<ConsumesRequestCondition.ConsumeRequestCondition> allConditions = parseContentTypeHeaders(headers);
|
||||||
|
|
@ -115,14 +124,65 @@ public abstract class RequestConditionFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ConsumesRequestCondition.ConsumeRequestCondition> parseContentTypeHeaders(String[] headers) {
|
private static List<ConsumesRequestCondition.ConsumeRequestCondition> parseContentTypeHeaders(String[] headers) {
|
||||||
List<ConsumesRequestCondition.ConsumeRequestCondition> allConditions =
|
List<ConsumesRequestCondition.ConsumeRequestCondition> conditions =
|
||||||
new ArrayList<ConsumesRequestCondition.ConsumeRequestCondition>();
|
new ArrayList<ConsumesRequestCondition.ConsumeRequestCondition>();
|
||||||
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
|
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
|
||||||
for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) {
|
for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) {
|
||||||
if (CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) {
|
if (CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) {
|
||||||
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerCondition.value);
|
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerCondition.value);
|
||||||
for (MediaType mediaType : mediaTypes) {
|
for (MediaType mediaType : mediaTypes) {
|
||||||
allConditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(mediaType,
|
conditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(mediaType,
|
||||||
|
headerCondition.isNegated));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given produces, and returns them as a single request condition.
|
||||||
|
*
|
||||||
|
* @param produces the produces
|
||||||
|
* @return the request condition
|
||||||
|
* @see org.springframework.web.bind.annotation.RequestMapping#produces()
|
||||||
|
*/
|
||||||
|
public static ProducesRequestCondition parseProduces(String... produces) {
|
||||||
|
return new ProducesRequestCondition(produces);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given produces and {@code Accept} headers, and returns them as a single request condition. <p>Only {@code
|
||||||
|
* Accept} headers will be used, all other headers will be ignored.
|
||||||
|
*
|
||||||
|
* @param produces the consumes
|
||||||
|
* @param headers the headers
|
||||||
|
* @return the request condition
|
||||||
|
* @see org.springframework.web.bind.annotation.RequestMapping#produces()
|
||||||
|
*/
|
||||||
|
public static ProducesRequestCondition parseProduces(String[] produces, String[] headers) {
|
||||||
|
|
||||||
|
List<ProducesRequestCondition.ProduceRequestCondition> allConditions = parseAcceptHeaders(headers);
|
||||||
|
|
||||||
|
// ignore the default consumes() value if any accept headers have been set
|
||||||
|
boolean headersHasAccept = !allConditions.isEmpty();
|
||||||
|
boolean producesHasDefaultValue = produces.length == 1 && produces[0].equals("*/*");
|
||||||
|
if (!headersHasAccept || !producesHasDefaultValue) {
|
||||||
|
for (String produce : produces) {
|
||||||
|
allConditions.add(new ProducesRequestCondition.ProduceRequestCondition(produce));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ProducesRequestCondition(allConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ProducesRequestCondition.ProduceRequestCondition> parseAcceptHeaders(String[] headers) {
|
||||||
|
List<ProducesRequestCondition.ProduceRequestCondition> allConditions =
|
||||||
|
new ArrayList<ProducesRequestCondition.ProduceRequestCondition>();
|
||||||
|
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
|
||||||
|
for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) {
|
||||||
|
if (ACCEPT_HEADER.equalsIgnoreCase(headerCondition.name)) {
|
||||||
|
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerCondition.value);
|
||||||
|
for (MediaType mediaType : mediaTypes) {
|
||||||
|
allConditions.add(new ProducesRequestCondition.ProduceRequestCondition(mediaType,
|
||||||
headerCondition.isNegated));
|
headerCondition.isNegated));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,4 +190,5 @@ public abstract class RequestConditionFactory {
|
||||||
return allConditions;
|
return allConditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
|
@ -100,7 +99,7 @@ public class RequestMappingInfoComparatorTests {
|
||||||
RequestMappingInfo empty = new RequestMappingInfo(null, null);
|
RequestMappingInfo empty = new RequestMappingInfo(null, null);
|
||||||
RequestMappingInfo oneMethod = new RequestMappingInfo(null, new RequestMethod[] {RequestMethod.GET});
|
RequestMappingInfo oneMethod = new RequestMappingInfo(null, new RequestMethod[] {RequestMethod.GET});
|
||||||
RequestMappingInfo oneMethodOneParam =
|
RequestMappingInfo oneMethodOneParam =
|
||||||
new RequestMappingInfo(null, RequestConditionFactory.parseMethods(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null, null);
|
new RequestMappingInfo(null, RequestConditionFactory.parseMethods(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null, null, null);
|
||||||
List<RequestMappingInfo> list = asList(empty, oneMethod, oneMethodOneParam);
|
List<RequestMappingInfo> list = asList(empty, oneMethod, oneMethodOneParam);
|
||||||
Collections.shuffle(list);
|
Collections.shuffle(list);
|
||||||
Collections.sort(list, handlerMapping.getMappingComparator("", request));
|
Collections.sort(list, handlerMapping.getMappingComparator("", request));
|
||||||
|
|
@ -111,16 +110,16 @@ public class RequestMappingInfoComparatorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore // TODO : remove ignore
|
public void produces() {
|
||||||
public void acceptHeaders() {
|
RequestMappingInfo html = new RequestMappingInfo(null, null, null, null, null, RequestConditionFactory.parseProduces("text/html"));
|
||||||
RequestMappingInfo html = new RequestMappingInfo(null, null, null, RequestConditionFactory.parseHeaders("accept=text/html"), null);
|
RequestMappingInfo xml = new RequestMappingInfo(null, null, null, null, null, RequestConditionFactory.parseProduces("application/xml"));
|
||||||
RequestMappingInfo xml = new RequestMappingInfo(null, null, null, RequestConditionFactory.parseHeaders("accept=application/xml"), null);
|
|
||||||
RequestMappingInfo none = new RequestMappingInfo(null, null);
|
RequestMappingInfo none = new RequestMappingInfo(null, null);
|
||||||
|
|
||||||
request.addHeader("Accept", "application/xml, text/html");
|
request.addHeader("Accept", "application/xml, text/html");
|
||||||
Comparator<RequestMappingInfo> comparator = handlerMapping.getMappingComparator("", request);
|
Comparator<RequestMappingInfo> comparator = handlerMapping.getMappingComparator("", request);
|
||||||
|
|
||||||
assertTrue(comparator.compare(html, xml) > 0);
|
int result = comparator.compare(html, xml);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||||
assertTrue(comparator.compare(xml, html) < 0);
|
assertTrue(comparator.compare(xml, html) < 0);
|
||||||
assertTrue(comparator.compare(xml, none) < 0);
|
assertTrue(comparator.compare(xml, none) < 0);
|
||||||
assertTrue(comparator.compare(none, xml) > 0);
|
assertTrue(comparator.compare(none, xml) > 0);
|
||||||
|
|
|
||||||
|
|
@ -181,13 +181,13 @@ public class RequestMappingInfoTests {
|
||||||
|
|
||||||
RequestMappingInfo key =
|
RequestMappingInfo key =
|
||||||
new RequestMappingInfo(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null,
|
new RequestMappingInfo(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null,
|
||||||
null);
|
null, null);
|
||||||
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
assertNotNull(match);
|
assertNotNull(match);
|
||||||
|
|
||||||
key = new RequestMappingInfo(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null,
|
key = new RequestMappingInfo(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null,
|
||||||
null);
|
null, null);
|
||||||
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
assertNull(match);
|
assertNull(match);
|
||||||
|
|
@ -202,13 +202,13 @@ public class RequestMappingInfoTests {
|
||||||
|
|
||||||
RequestMappingInfo key =
|
RequestMappingInfo key =
|
||||||
new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"),
|
new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"),
|
||||||
null);
|
null, null);
|
||||||
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
assertNotNull(match);
|
assertNotNull(match);
|
||||||
|
|
||||||
key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"),
|
key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"),
|
||||||
null);
|
null, null);
|
||||||
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
assertNull(match);
|
assertNull(match);
|
||||||
|
|
@ -222,13 +222,33 @@ public class RequestMappingInfoTests {
|
||||||
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
|
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
|
||||||
|
|
||||||
RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null,
|
RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null,
|
||||||
RequestConditionFactory.parseConsumes("text/plain"));
|
RequestConditionFactory.parseConsumes("text/plain"), null);
|
||||||
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
assertNotNull(match);
|
assertNotNull(match);
|
||||||
|
|
||||||
key = new RequestMappingInfo(singleton("/foo"), null, null, null,
|
key = new RequestMappingInfo(singleton("/foo"), null, null, null,
|
||||||
RequestConditionFactory.parseConsumes("application/xml"));
|
RequestConditionFactory.parseConsumes("application/xml"), null);
|
||||||
|
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
|
assertNull(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void producesCondition() {
|
||||||
|
PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||||
|
request.addHeader("Accept", "text/plain");
|
||||||
|
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
|
||||||
|
|
||||||
|
RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null,
|
||||||
|
null, RequestConditionFactory.parseProduces("text/plain"));
|
||||||
|
RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
|
assertNotNull(match);
|
||||||
|
|
||||||
|
key = new RequestMappingInfo(singleton("/foo"), null, null, null, null,
|
||||||
|
RequestConditionFactory.parseProduces("application/xml"));
|
||||||
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||||
|
|
||||||
assertNull(match);
|
assertNull(match);
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.mvc.method.annotation;
|
package org.springframework.web.servlet.mvc.method.annotation;
|
||||||
|
|
||||||
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.assertSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.beans.PropertyEditorSupport;
|
import java.beans.PropertyEditorSupport;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
@ -48,7 +40,6 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.ServletConfig;
|
import javax.servlet.ServletConfig;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
|
@ -60,8 +51,8 @@ import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||||
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
||||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||||
|
|
@ -142,6 +133,8 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebA
|
||||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The origin of this test fixture is {@link ServletHandlerMethodTests} with tests in this class adapted to run
|
* The origin of this test fixture is {@link ServletHandlerMethodTests} with tests in this class adapted to run
|
||||||
* against the HandlerMethod infrastructure rather than against the DefaultAnnotationHandlerMapping, the
|
* against the HandlerMethod infrastructure rather than against the DefaultAnnotationHandlerMapping, the
|
||||||
|
|
@ -1033,8 +1026,6 @@ public class ServletHandlerMethodTests {
|
||||||
assertEquals("non-pdf", response.getContentAsString());
|
assertEquals("non-pdf", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: uncomment ignore
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void acceptHeaders() throws ServletException, IOException {
|
public void acceptHeaders() throws ServletException, IOException {
|
||||||
initDispatcherServlet(AcceptHeadersController.class, null);
|
initDispatcherServlet(AcceptHeadersController.class, null);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.servlet.mvc.method.condition;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
*/
|
||||||
|
public class ProducesRequestConditionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consumesMatch() {
|
||||||
|
RequestCondition condition = new ProducesRequestCondition("text/plain");
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Accept", "text/plain");
|
||||||
|
|
||||||
|
assertTrue(condition.match(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void negatedConsumesMatch() {
|
||||||
|
RequestCondition condition = new ProducesRequestCondition("!text/plain");
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Accept", "text/plain");
|
||||||
|
|
||||||
|
assertFalse(condition.match(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consumesWildcardMatch() {
|
||||||
|
RequestCondition condition = new ProducesRequestCondition("text/*");
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Accept", "text/plain");
|
||||||
|
|
||||||
|
assertTrue(condition.match(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consumesMultipleMatch() {
|
||||||
|
RequestCondition condition = new ProducesRequestCondition("text/plain", "application/xml");
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Accept", "text/plain");
|
||||||
|
|
||||||
|
assertTrue(condition.match(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consumesSingleNoMatch() {
|
||||||
|
RequestCondition condition = new ProducesRequestCondition("text/plain");
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Accept", "application/xml");
|
||||||
|
|
||||||
|
assertFalse(condition.match(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compareToSingle() {
|
||||||
|
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||||
|
ProducesRequestCondition condition2 = new ProducesRequestCondition("text/*");
|
||||||
|
|
||||||
|
List<MediaType> accept = Collections.singletonList(MediaType.TEXT_PLAIN);
|
||||||
|
|
||||||
|
int result = condition1.compareTo(condition2, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||||
|
|
||||||
|
result = condition2.compareTo(condition1, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compareToMultiple() {
|
||||||
|
ProducesRequestCondition condition1 = new ProducesRequestCondition("*/*", "text/plain");
|
||||||
|
ProducesRequestCondition condition2 = new ProducesRequestCondition("text/*", "text/plain;q=0.7");
|
||||||
|
|
||||||
|
List<MediaType> accept = Collections.singletonList(MediaType.TEXT_PLAIN);
|
||||||
|
|
||||||
|
int result = condition1.compareTo(condition2, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||||
|
|
||||||
|
result = condition2.compareTo(condition1, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||||
|
|
||||||
|
condition1 = new ProducesRequestCondition("*/*");
|
||||||
|
condition2 = new ProducesRequestCondition("text/*");
|
||||||
|
|
||||||
|
accept = Collections.singletonList(new MediaType("text", "*"));
|
||||||
|
|
||||||
|
result = condition1.compareTo(condition2, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||||
|
|
||||||
|
result = condition2.compareTo(condition1, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compareToMultipleAccept() {
|
||||||
|
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/*", "text/plain");
|
||||||
|
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/*", "application/xml");
|
||||||
|
|
||||||
|
List<MediaType> accept = Arrays.asList(MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML);
|
||||||
|
|
||||||
|
int result = condition1.compareTo(condition2, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||||
|
|
||||||
|
result = condition2.compareTo(condition1, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||||
|
|
||||||
|
accept = Arrays.asList(MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN);
|
||||||
|
|
||||||
|
result = condition1.compareTo(condition2, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result > 0);
|
||||||
|
|
||||||
|
result = condition2.compareTo(condition1, accept);
|
||||||
|
assertTrue("Invalid comparison result: " + result, result < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void combine() {
|
||||||
|
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||||
|
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/xml");
|
||||||
|
|
||||||
|
ProducesRequestCondition result = condition1.combine(condition2);
|
||||||
|
assertEquals(condition2, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void combineWithDefault() {
|
||||||
|
ProducesRequestCondition condition1 = new ProducesRequestCondition("text/plain");
|
||||||
|
ProducesRequestCondition condition2 = new ProducesRequestCondition("*/*");
|
||||||
|
|
||||||
|
ProducesRequestCondition result = condition1.combine(condition2);
|
||||||
|
assertEquals(condition1, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseConsumesAndHeaders() {
|
||||||
|
String[] consumes = new String[] {"text/plain"};
|
||||||
|
String[] headers = new String[]{"foo=bar", "accept=application/xml,application/pdf"};
|
||||||
|
ProducesRequestCondition condition = RequestConditionFactory.parseProduces(consumes, headers);
|
||||||
|
|
||||||
|
assertConditions(condition, "text/plain", "application/xml", "application/pdf");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseConsumesDefault() {
|
||||||
|
String[] consumes = new String[] {"*/*"};
|
||||||
|
String[] headers = new String[0];
|
||||||
|
ProducesRequestCondition condition = RequestConditionFactory.parseProduces(consumes, headers);
|
||||||
|
|
||||||
|
assertConditions(condition, "*/*");
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void parseConsumesDefaultAndHeaders() {
|
||||||
|
String[] consumes = new String[] {"*/*"};
|
||||||
|
String[] headers = new String[]{"foo=bar", "accept=text/plain"};
|
||||||
|
ProducesRequestCondition condition = RequestConditionFactory.parseProduces(consumes, headers);
|
||||||
|
|
||||||
|
assertConditions(condition, "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMatchingCondition() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Accept", "text/plain");
|
||||||
|
|
||||||
|
ProducesRequestCondition condition = new ProducesRequestCondition("text/plain", "application/xml");
|
||||||
|
|
||||||
|
ProducesRequestCondition result = condition.getMatchingCondition(request);
|
||||||
|
assertConditions(result, "text/plain");
|
||||||
|
|
||||||
|
condition = new ProducesRequestCondition("application/xml");
|
||||||
|
|
||||||
|
result = condition.getMatchingCondition(request);
|
||||||
|
assertNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertConditions(ProducesRequestCondition condition, String... expected) {
|
||||||
|
Set<ProducesRequestCondition.ProduceRequestCondition> conditions = condition.getConditions();
|
||||||
|
assertEquals("Invalid amount of conditions", conditions.size(), expected.length);
|
||||||
|
for (String s : expected) {
|
||||||
|
boolean found = false;
|
||||||
|
for (ProducesRequestCondition.ProduceRequestCondition requestCondition : conditions) {
|
||||||
|
String conditionMediaType = requestCondition.getMediaType().toString();
|
||||||
|
if (conditionMediaType.equals(s)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
fail("Condition [" + s + "] not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -311,4 +311,17 @@ public @interface RequestMapping {
|
||||||
*/
|
*/
|
||||||
String[] consumes() default "*/*";
|
String[] consumes() default "*/*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The producible media types of the mapped request, narrowing the primary mapping.
|
||||||
|
* <p>The format is a sequence of media types ("text/plain", "application/*),
|
||||||
|
* with a request only mapped if the {@code Accept} matches one of these media types.
|
||||||
|
* Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
|
||||||
|
* all requests with a {@code Accept} other than "text/plain".
|
||||||
|
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||||
|
* When used at the type level, all method-level mappings override
|
||||||
|
* this consumes restriction.
|
||||||
|
* @see org.springframework.http.MediaType
|
||||||
|
*/
|
||||||
|
String[] produces() default "*/*";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue