SPR-5251: URI Templates in @RequestMapping
This commit is contained in:
@@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.util;
|
package org.springframework.util;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PathMatcher implementation for Ant-style path patterns.
|
* PathMatcher implementation for Ant-style path patterns.
|
||||||
* Examples are provided below.
|
* Examples are provided below.
|
||||||
@@ -56,6 +61,10 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
/** Default path separator: "/" */
|
/** Default path separator: "/" */
|
||||||
public static final String DEFAULT_PATH_SEPARATOR = "/";
|
public static final String DEFAULT_PATH_SEPARATOR = "/";
|
||||||
|
|
||||||
|
/** Captures URI template variable names. */
|
||||||
|
private static final Pattern URI_TEMPLATE_NAMES_PATTERN = Pattern.compile("\\{([\\w-~_\\.]+?)\\}");
|
||||||
|
|
||||||
|
|
||||||
private String pathSeparator = DEFAULT_PATH_SEPARATOR;
|
private String pathSeparator = DEFAULT_PATH_SEPARATOR;
|
||||||
|
|
||||||
|
|
||||||
@@ -91,6 +100,7 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
* <code>false</code> if it didn't
|
* <code>false</code> if it didn't
|
||||||
*/
|
*/
|
||||||
protected boolean doMatch(String pattern, String path, boolean fullMatch) {
|
protected boolean doMatch(String pattern, String path, boolean fullMatch) {
|
||||||
|
pattern = uriTemplateToAntPattern(pattern);
|
||||||
if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
|
if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -119,14 +129,13 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
if (pathIdxStart > pathIdxEnd) {
|
if (pathIdxStart > pathIdxEnd) {
|
||||||
// Path is exhausted, only match if rest of pattern is * or **'s
|
// Path is exhausted, only match if rest of pattern is * or **'s
|
||||||
if (pattIdxStart > pattIdxEnd) {
|
if (pattIdxStart > pattIdxEnd) {
|
||||||
return (pattern.endsWith(this.pathSeparator) ?
|
return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) :
|
||||||
path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator));
|
!path.endsWith(this.pathSeparator));
|
||||||
}
|
}
|
||||||
if (!fullMatch) {
|
if (!fullMatch) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") &&
|
if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
|
||||||
path.endsWith(this.pathSeparator)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
|
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
|
||||||
@@ -187,17 +196,17 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
int foundIdx = -1;
|
int foundIdx = -1;
|
||||||
|
|
||||||
strLoop:
|
strLoop:
|
||||||
for (int i = 0; i <= strLength - patLength; i++) {
|
for (int i = 0; i <= strLength - patLength; i++) {
|
||||||
for (int j = 0; j < patLength; j++) {
|
for (int j = 0; j < patLength; j++) {
|
||||||
String subPat = (String) pattDirs[pattIdxStart + j + 1];
|
String subPat = (String) pattDirs[pattIdxStart + j + 1];
|
||||||
String subStr = (String) pathDirs[pathIdxStart + i + j];
|
String subStr = (String) pathDirs[pathIdxStart + i + j];
|
||||||
if (!matchStrings(subPat, subStr)) {
|
if (!matchStrings(subPat, subStr)) {
|
||||||
continue strLoop;
|
continue strLoop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foundIdx = pathIdxStart + i;
|
foundIdx = pathIdxStart + i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundIdx == -1) {
|
if (foundIdx == -1) {
|
||||||
return false;
|
return false;
|
||||||
@@ -382,7 +391,7 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
|
String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
|
||||||
String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
|
String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
|
||||||
|
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
// Add any path parts that have a wildcarded pattern part.
|
// Add any path parts that have a wildcarded pattern part.
|
||||||
int puts = 0;
|
int puts = 0;
|
||||||
@@ -390,9 +399,9 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
String patternPart = patternParts[i];
|
String patternPart = patternParts[i];
|
||||||
if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {
|
if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {
|
||||||
if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {
|
if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {
|
||||||
buffer.append(this.pathSeparator);
|
builder.append(this.pathSeparator);
|
||||||
}
|
}
|
||||||
buffer.append(pathParts[i]);
|
builder.append(pathParts[i]);
|
||||||
puts++;
|
puts++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,12 +409,51 @@ public class AntPathMatcher implements PathMatcher {
|
|||||||
// Append any trailing path parts.
|
// Append any trailing path parts.
|
||||||
for (int i = patternParts.length; i < pathParts.length; i++) {
|
for (int i = patternParts.length; i < pathParts.length; i++) {
|
||||||
if (puts > 0 || i > 0) {
|
if (puts > 0 || i > 0) {
|
||||||
buffer.append(this.pathSeparator);
|
builder.append(this.pathSeparator);
|
||||||
}
|
}
|
||||||
buffer.append(pathParts[i]);
|
builder.append(pathParts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.toString();
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces URI template variables with Ant-style pattern patchs. Looks for variables within curly braces, and replaces
|
||||||
|
* those with <code>*</code>.
|
||||||
|
*
|
||||||
|
* <p/>For example: <code>/hotels/{hotel}/bookings</code> becomes
|
||||||
|
* <code>/hotels/*/bookings</code>
|
||||||
|
*
|
||||||
|
* @param pattern the pattern, possibly containing URI template variables
|
||||||
|
* @return the Ant-stlye pattern path
|
||||||
|
* @see org.springframework.util.AntPathMatcher
|
||||||
|
*/
|
||||||
|
private static String uriTemplateToAntPattern(String pattern) {
|
||||||
|
Matcher matcher = URI_TEMPLATE_NAMES_PATTERN.matcher(pattern);
|
||||||
|
return matcher.replaceAll("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
|
||||||
|
if (pattern.contains("**") && pattern.contains("{")) {
|
||||||
|
throw new IllegalArgumentException("Combining '**' and URI templates is not allowed");
|
||||||
|
}
|
||||||
|
String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
|
||||||
|
String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
|
||||||
|
|
||||||
|
Map<String, String> variables = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
|
for (int i = 0; i < patternParts.length && i < pathParts.length; i++) {
|
||||||
|
String patternPart = patternParts[i];
|
||||||
|
String pathPart = pathParts[i];
|
||||||
|
int patternEnd = patternPart.length() -1 ;
|
||||||
|
if (patternEnd > 1 && patternPart.charAt(0) == '{' && patternPart.charAt(patternEnd) == '}') {
|
||||||
|
String varName = patternPart.substring(1, patternEnd);
|
||||||
|
variables.put(varName, pathPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.util;
|
package org.springframework.util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strategy interface for <code>String</code>-based path matching.
|
* Strategy interface for <code>String</code>-based path matching.
|
||||||
*
|
*
|
||||||
@@ -88,4 +90,16 @@ public interface PathMatcher {
|
|||||||
*/
|
*/
|
||||||
String extractPathWithinPattern(String pattern, String path);
|
String extractPathWithinPattern(String pattern, String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a pattern and a full path, extract the URI template variables. URI template
|
||||||
|
* variables are expressed through curly brackets ('{' and '}').
|
||||||
|
*
|
||||||
|
* <p>For example: For pattern "/hotels/{hotel}" and path "/hotels/1", this method will
|
||||||
|
* return a map containing "hotel"->"1".
|
||||||
|
*
|
||||||
|
* @param pattern the path pattern, possibly containing URI templates
|
||||||
|
* @param path the full path to extract template variables from
|
||||||
|
* @return a map, containing variable names as keys; variables values as values
|
||||||
|
*/
|
||||||
|
Map<String, String> extractUriTemplateVariables(String pattern, String path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,31 @@
|
|||||||
|
|
||||||
package org.springframework.util;
|
package org.springframework.util;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alef Arendsen
|
* @author Alef Arendsen
|
||||||
* @author Seth Ladd
|
* @author Seth Ladd
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Arjen Poutsma
|
||||||
*/
|
*/
|
||||||
public class PathMatcherTests extends TestCase {
|
public class AntPathMatcherTests {
|
||||||
|
|
||||||
public void testAntPathMatcher() {
|
private AntPathMatcher pathMatcher;
|
||||||
PathMatcher pathMatcher = new AntPathMatcher();
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void createMatcher() {
|
||||||
|
pathMatcher = new AntPathMatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void standard() {
|
||||||
// test exact matching
|
// test exact matching
|
||||||
assertTrue(pathMatcher.match("test", "test"));
|
assertTrue(pathMatcher.match("test", "test"));
|
||||||
assertTrue(pathMatcher.match("/test", "/test"));
|
assertTrue(pathMatcher.match("/test", "/test"));
|
||||||
@@ -109,9 +122,8 @@ public class PathMatcherTests extends TestCase {
|
|||||||
assertTrue(pathMatcher.match("", ""));
|
assertTrue(pathMatcher.match("", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAntPathMatcherWithMatchStart() {
|
@Test
|
||||||
PathMatcher pathMatcher = new AntPathMatcher();
|
public void withMatchStart() {
|
||||||
|
|
||||||
// test exact matching
|
// test exact matching
|
||||||
assertTrue(pathMatcher.matchStart("test", "test"));
|
assertTrue(pathMatcher.matchStart("test", "test"));
|
||||||
assertTrue(pathMatcher.matchStart("/test", "/test"));
|
assertTrue(pathMatcher.matchStart("/test", "/test"));
|
||||||
@@ -197,8 +209,8 @@ public class PathMatcherTests extends TestCase {
|
|||||||
assertTrue(pathMatcher.matchStart("", ""));
|
assertTrue(pathMatcher.matchStart("", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAntPathMatcherWithUniqueDeliminator() {
|
@Test
|
||||||
AntPathMatcher pathMatcher = new AntPathMatcher();
|
public void uniqueDeliminator() {
|
||||||
pathMatcher.setPathSeparator(".");
|
pathMatcher.setPathSeparator(".");
|
||||||
|
|
||||||
// test exact matching
|
// test exact matching
|
||||||
@@ -259,9 +271,8 @@ public class PathMatcherTests extends TestCase {
|
|||||||
assertFalse(pathMatcher.match(".*bla.test", "XXXbl.test"));
|
assertFalse(pathMatcher.match(".*bla.test", "XXXbl.test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAntPathMatcherExtractPathWithinPattern() throws Exception {
|
@Test
|
||||||
PathMatcher pathMatcher = new AntPathMatcher();
|
public void extractPathWithinPattern() throws Exception {
|
||||||
|
|
||||||
assertEquals("", pathMatcher.extractPathWithinPattern("/docs/commit.html", "/docs/commit.html"));
|
assertEquals("", pathMatcher.extractPathWithinPattern("/docs/commit.html", "/docs/commit.html"));
|
||||||
|
|
||||||
assertEquals("cvs/commit", pathMatcher.extractPathWithinPattern("/docs/*", "/docs/cvs/commit"));
|
assertEquals("cvs/commit", pathMatcher.extractPathWithinPattern("/docs/*", "/docs/cvs/commit"));
|
||||||
@@ -282,4 +293,17 @@ public class PathMatcherTests extends TestCase {
|
|||||||
assertEquals("docs/cvs/commit.html", pathMatcher.extractPathWithinPattern("/d?cs/**/*.html", "/docs/cvs/commit.html"));
|
assertEquals("docs/cvs/commit.html", pathMatcher.extractPathWithinPattern("/d?cs/**/*.html", "/docs/cvs/commit.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractUriTemplateVariables() throws Exception {
|
||||||
|
Map<String,String> result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}", "/hotels/1");
|
||||||
|
assertEquals(Collections.singletonMap("hotel", "1"), result);
|
||||||
|
|
||||||
|
result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}/bookings/{booking}", "/hotels/1/bookings/2");
|
||||||
|
Map<String, String> expected = new LinkedHashMap<String, String>();
|
||||||
|
expected.put("hotel", "1");
|
||||||
|
expected.put("booking", "2");
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package org.springframework.web.bind.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation which indicates that a method parameter should be bound to a URI template variable. Supported for {@link
|
||||||
|
* RequestMapping} annotated handler methods in Servlet environments.
|
||||||
|
*
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
* @see RequestMapping
|
||||||
|
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
@Target(ElementType.PARAMETER)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface PathVariable {
|
||||||
|
|
||||||
|
/** The URI template variable to bind to. */
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -72,6 +72,9 @@ import java.lang.annotation.Target;
|
|||||||
* <li>{@link RequestParam @RequestParam} annotated parameters for access to
|
* <li>{@link RequestParam @RequestParam} annotated parameters for access to
|
||||||
* specific Servlet/Portlet request parameters. Parameter values will be
|
* specific Servlet/Portlet request parameters. Parameter values will be
|
||||||
* converted to the declared method argument type.
|
* converted to the declared method argument type.
|
||||||
|
* <li>{@link PathVariable @PathVariable} annotated parameters for acces to
|
||||||
|
* URI template values (i.e. /hotels/{hotel}). Variable values will be
|
||||||
|
* converted to the declared method argument type.
|
||||||
* <li>{@link java.util.Map} / {@link org.springframework.ui.Model} /
|
* <li>{@link java.util.Map} / {@link org.springframework.ui.Model} /
|
||||||
* {@link org.springframework.ui.ModelMap} for enriching the implicit model
|
* {@link org.springframework.ui.ModelMap} for enriching the implicit model
|
||||||
* that will be exposed to the web view.
|
* that will be exposed to the web view.
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import org.springframework.validation.Errors;
|
|||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||||
@@ -158,11 +159,11 @@ public class HandlerMethodInvoker {
|
|||||||
String paramName = null;
|
String paramName = null;
|
||||||
boolean paramRequired = false;
|
boolean paramRequired = false;
|
||||||
String paramDefaultValue = null;
|
String paramDefaultValue = null;
|
||||||
|
String pathVarName = null;
|
||||||
String attrName = null;
|
String attrName = null;
|
||||||
Object[] paramAnns = methodParam.getParameterAnnotations();
|
Object[] paramAnns = methodParam.getParameterAnnotations();
|
||||||
|
|
||||||
for (int j = 0; j < paramAnns.length; j++) {
|
for (Object paramAnn : paramAnns) {
|
||||||
Object paramAnn = paramAnns[j];
|
|
||||||
if (RequestParam.class.isInstance(paramAnn)) {
|
if (RequestParam.class.isInstance(paramAnn)) {
|
||||||
RequestParam requestParam = (RequestParam) paramAnn;
|
RequestParam requestParam = (RequestParam) paramAnn;
|
||||||
paramName = requestParam.value();
|
paramName = requestParam.value();
|
||||||
@@ -173,21 +174,24 @@ public class HandlerMethodInvoker {
|
|||||||
else if (ModelAttribute.class.isInstance(paramAnn)) {
|
else if (ModelAttribute.class.isInstance(paramAnn)) {
|
||||||
ModelAttribute attr = (ModelAttribute) paramAnn;
|
ModelAttribute attr = (ModelAttribute) paramAnn;
|
||||||
attrName = attr.value();
|
attrName = attr.value();
|
||||||
|
} else if (PathVariable.class.isInstance(paramAnn)) {
|
||||||
|
PathVariable pathVar = (PathVariable) paramAnn;
|
||||||
|
pathVarName = pathVar.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (paramName != null && attrName != null) {
|
if ((paramName != null && attrName != null) || (paramName != null && pathVarName != null) ||
|
||||||
throw new IllegalStateException("@RequestParam and @ModelAttribute are an exclusive choice -" +
|
(pathVarName != null && attrName != null)) {
|
||||||
"do not specify both on the same parameter: " + handlerMethod);
|
throw new IllegalStateException("@RequestParam, @PathVariable and @ModelAttribute are exclusive " +
|
||||||
|
"choices - do not specify both on the same parameter: " + handlerMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
Class paramType = methodParam.getParameterType();
|
if (paramName == null && attrName == null && pathVarName == null) {
|
||||||
|
|
||||||
if (paramName == null && attrName == null) {
|
|
||||||
Object argValue = resolveCommonArgument(methodParam, webRequest);
|
Object argValue = resolveCommonArgument(methodParam, webRequest);
|
||||||
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
||||||
args[i] = argValue;
|
args[i] = argValue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Class paramType = methodParam.getParameterType();
|
||||||
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
|
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
|
||||||
args[i] = implicitModel;
|
args[i] = implicitModel;
|
||||||
}
|
}
|
||||||
@@ -223,13 +227,15 @@ public class HandlerMethodInvoker {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
implicitModel.putAll(binder.getBindingResult().getModel());
|
implicitModel.putAll(binder.getBindingResult().getModel());
|
||||||
|
} else if (pathVarName != null) {
|
||||||
|
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
|
protected void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
if (this.bindingInitializer != null) {
|
if (this.bindingInitializer != null) {
|
||||||
@@ -276,8 +282,7 @@ public class HandlerMethodInvoker {
|
|||||||
String paramDefaultValue = null;
|
String paramDefaultValue = null;
|
||||||
Object[] paramAnns = methodParam.getParameterAnnotations();
|
Object[] paramAnns = methodParam.getParameterAnnotations();
|
||||||
|
|
||||||
for (int j = 0; j < paramAnns.length; j++) {
|
for (Object paramAnn : paramAnns) {
|
||||||
Object paramAnn = paramAnns[j];
|
|
||||||
if (RequestParam.class.isInstance(paramAnn)) {
|
if (RequestParam.class.isInstance(paramAnn)) {
|
||||||
RequestParam requestParam = (RequestParam) paramAnn;
|
RequestParam requestParam = (RequestParam) paramAnn;
|
||||||
paramName = requestParam.value();
|
paramName = requestParam.value();
|
||||||
@@ -328,7 +333,7 @@ public class HandlerMethodInvoker {
|
|||||||
Object handlerForInitBinderCall) throws Exception {
|
Object handlerForInitBinderCall) throws Exception {
|
||||||
|
|
||||||
Class paramType = methodParam.getParameterType();
|
Class paramType = methodParam.getParameterType();
|
||||||
if ("".equals(paramName)) {
|
if (paramName.length() == 0) {
|
||||||
paramName = methodParam.getParameterName();
|
paramName = methodParam.getParameterName();
|
||||||
if (paramName == null) {
|
if (paramName == null) {
|
||||||
throw new IllegalStateException("No parameter specified for @RequestParam argument of type [" +
|
throw new IllegalStateException("No parameter specified for @RequestParam argument of type [" +
|
||||||
@@ -393,6 +398,18 @@ public class HandlerMethodInvoker {
|
|||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the given {@link org.springframework.web.bind.annotation.PathVariable @PathVariable} variable. Overriden in
|
||||||
|
* {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.ServletHandlerMethodInvoker},
|
||||||
|
* throws an UnsupportedOperationException by default.
|
||||||
|
*/
|
||||||
|
protected Object resolvePathVariable(String pathVarName,
|
||||||
|
MethodParameter methodParam,
|
||||||
|
NativeWebRequest webRequest,
|
||||||
|
Object handlerForInitBinderCall) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("@PathVariable not supported");
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final void updateModelAttributes(Object handler,
|
public final void updateModelAttributes(Object handler,
|
||||||
Map mavModel,
|
Map mavModel,
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ public interface HandlerMapping {
|
|||||||
*/
|
*/
|
||||||
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
|
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@link HttpServletRequest} attribute that contains the URI
|
||||||
|
* templates map, mapping variable names to values.
|
||||||
|
* <p>Note: This attribute is not required to be supported by all
|
||||||
|
* HandlerMapping implementations. URL-based HandlerMappings will
|
||||||
|
* typically support it, but handlers should not necessarily expect
|
||||||
|
* this request attribute to be present in all scenarios.
|
||||||
|
*/
|
||||||
|
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a handler and any interceptors for this request. The choice may be made
|
* Return a handler and any interceptors for this request. The choice may be made
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package org.springframework.web.servlet.handler;
|
package org.springframework.web.servlet.handler;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -26,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.PathMatcher;
|
import org.springframework.util.PathMatcher;
|
||||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
@@ -47,6 +47,7 @@ import org.springframework.web.util.UrlPathHelper;
|
|||||||
* path pattern that matches the current request path.
|
* path pattern that matches the current request path.
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Arjen Poutsma
|
||||||
* @since 16.04.2003
|
* @since 16.04.2003
|
||||||
* @see #setAlwaysUseFullPath
|
* @see #setAlwaysUseFullPath
|
||||||
* @see #setUrlDecode
|
* @see #setUrlDecode
|
||||||
@@ -62,7 +63,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
|
|
||||||
private boolean lazyInitHandlers = false;
|
private boolean lazyInitHandlers = false;
|
||||||
|
|
||||||
private final Map handlerMap = new LinkedHashMap();
|
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,7 +171,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
}
|
}
|
||||||
if (rawHandler != null) {
|
if (rawHandler != null) {
|
||||||
validateHandler(rawHandler, request);
|
validateHandler(rawHandler, request);
|
||||||
handler = buildPathExposingHandler(rawHandler, lookupPath);
|
handler = buildPathExposingHandler(rawHandler, lookupPath, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (handler != null && logger.isDebugEnabled()) {
|
if (handler != null && logger.isDebugEnabled()) {
|
||||||
@@ -200,12 +201,11 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
Object handler = this.handlerMap.get(urlPath);
|
Object handler = this.handlerMap.get(urlPath);
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
validateHandler(handler, request);
|
validateHandler(handler, request);
|
||||||
return buildPathExposingHandler(handler, urlPath);
|
return buildPathExposingHandler(handler, urlPath, null);
|
||||||
}
|
}
|
||||||
// Pattern match?
|
// Pattern match?
|
||||||
String bestPathMatch = null;
|
String bestPathMatch = null;
|
||||||
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
|
for (String registeredPath : this.handlerMap.keySet()) {
|
||||||
String registeredPath = (String) it.next();
|
|
||||||
if (getPathMatcher().match(registeredPath, urlPath) &&
|
if (getPathMatcher().match(registeredPath, urlPath) &&
|
||||||
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
|
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
|
||||||
bestPathMatch = registeredPath;
|
bestPathMatch = registeredPath;
|
||||||
@@ -215,7 +215,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
handler = this.handlerMap.get(bestPathMatch);
|
handler = this.handlerMap.get(bestPathMatch);
|
||||||
validateHandler(handler, request);
|
validateHandler(handler, request);
|
||||||
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
|
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
|
||||||
return buildPathExposingHandler(handler, pathWithinMapping);
|
Map<String, String> uriTemplateVariables =
|
||||||
|
getPathMatcher().extractUriTemplateVariables(bestPathMatch, urlPath);
|
||||||
|
return buildPathExposingHandler(handler, pathWithinMapping, uriTemplateVariables);
|
||||||
}
|
}
|
||||||
// No handler found...
|
// No handler found...
|
||||||
return null;
|
return null;
|
||||||
@@ -234,15 +236,18 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a handler object for the given raw handler, exposing the actual
|
* Build a handler object for the given raw handler, exposing the actual
|
||||||
* handler as well as the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}
|
* handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as
|
||||||
* before executing the handler.
|
* the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
|
||||||
* <p>The default implementation builds a {@link HandlerExecutionChain}
|
* <p>The default implementation builds a {@link HandlerExecutionChain}
|
||||||
* with a special interceptor that exposes the path attribute.
|
* with a special interceptor that exposes the path attribute and uri template variables
|
||||||
* @param rawHandler the raw handler to expose
|
* @param rawHandler the raw handler to expose
|
||||||
* @param pathWithinMapping the path to expose before executing the handler
|
* @param pathWithinMapping the path to expose before executing the handler
|
||||||
|
* @param uriTemplateVariables the URI template variables, can be <code>null</code> if no variables found
|
||||||
* @return the final handler object
|
* @return the final handler object
|
||||||
*/
|
*/
|
||||||
protected Object buildPathExposingHandler(Object rawHandler, String pathWithinMapping) {
|
protected Object buildPathExposingHandler(Object rawHandler,
|
||||||
|
String pathWithinMapping,
|
||||||
|
Map<String, String> uriTemplateVariables) {
|
||||||
// Bean name or resolved handler?
|
// Bean name or resolved handler?
|
||||||
if (rawHandler instanceof String) {
|
if (rawHandler instanceof String) {
|
||||||
String handlerName = (String) rawHandler;
|
String handlerName = (String) rawHandler;
|
||||||
@@ -250,6 +255,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
}
|
}
|
||||||
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
|
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
|
||||||
chain.addInterceptor(new PathExposingHandlerInterceptor(pathWithinMapping));
|
chain.addInterceptor(new PathExposingHandlerInterceptor(pathWithinMapping));
|
||||||
|
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
|
||||||
|
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
|
||||||
|
}
|
||||||
return chain;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +271,15 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
|
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose the URI templates variables as request attribute.
|
||||||
|
* @param uriTemplateVariables the URI template variables
|
||||||
|
* @param request the request to expose the path to
|
||||||
|
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
|
||||||
|
*/
|
||||||
|
protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
|
||||||
|
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the specified handler for the given URL paths.
|
* Register the specified handler for the given URL paths.
|
||||||
@@ -273,8 +290,8 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
*/
|
*/
|
||||||
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
|
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
|
||||||
Assert.notNull(urlPaths, "URL path array must not be null");
|
Assert.notNull(urlPaths, "URL path array must not be null");
|
||||||
for (int j = 0; j < urlPaths.length; j++) {
|
for (String urlPath : urlPaths) {
|
||||||
registerHandler(urlPaths[j], beanName);
|
registerHandler(urlPath, beanName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +367,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
|
|
||||||
private final String pathWithinMapping;
|
private final String pathWithinMapping;
|
||||||
|
|
||||||
public PathExposingHandlerInterceptor(String pathWithinMapping) {
|
private PathExposingHandlerInterceptor(String pathWithinMapping) {
|
||||||
this.pathWithinMapping = pathWithinMapping;
|
this.pathWithinMapping = pathWithinMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,4 +378,24 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special interceptor for exposing the
|
||||||
|
* {@link AbstractUrlHandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE} attribute.
|
||||||
|
* @link AbstractUrlHandlerMapping#exposePathWithinMapping
|
||||||
|
*/
|
||||||
|
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
|
private final Map<String, String> uriTemplateVariables;
|
||||||
|
|
||||||
|
private UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
|
||||||
|
this.uriTemplateVariables = uriTemplateVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
|
exposeUriTemplateVariables(this.uriTemplateVariables, request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import org.springframework.web.bind.support.WebBindingInitializer;
|
|||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
import org.springframework.web.servlet.HandlerAdapter;
|
import org.springframework.web.servlet.HandlerAdapter;
|
||||||
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver;
|
import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver;
|
||||||
@@ -420,9 +421,12 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servlet-specific subclass of {@link HandlerMethodResolver}.
|
||||||
|
*/
|
||||||
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
|
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
|
||||||
|
|
||||||
public ServletHandlerMethodResolver(Class<?> handlerType) {
|
private ServletHandlerMethodResolver(Class<?> handlerType) {
|
||||||
super(handlerType);
|
super(handlerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,6 +573,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servlet-specific subclass of {@link HandlerMethodInvoker}.
|
||||||
|
*/
|
||||||
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
|
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
|
||||||
|
|
||||||
private boolean responseArgumentUsed = false;
|
private boolean responseArgumentUsed = false;
|
||||||
@@ -607,6 +614,33 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({"unchecked"})
|
||||||
|
protected Object resolvePathVariable(String pathVarName,
|
||||||
|
MethodParameter methodParam,
|
||||||
|
NativeWebRequest webRequest,
|
||||||
|
Object handlerForInitBinderCall) throws Exception {
|
||||||
|
Class paramType = methodParam.getParameterType();
|
||||||
|
if (pathVarName.length() == 0) {
|
||||||
|
pathVarName = methodParam.getParameterName();
|
||||||
|
if (pathVarName == null) {
|
||||||
|
throw new IllegalStateException("No variable name specified for @PathVariable argument of type [" +
|
||||||
|
paramType.getName() + "], and no parameter name information found in class file either.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest();
|
||||||
|
Map<String, String> uriTemplateVariables =
|
||||||
|
(Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||||
|
if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
|
||||||
|
throw new IllegalStateException("Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
|
||||||
|
}
|
||||||
|
String pathVarValue = uriTemplateVariables.get(pathVarName);
|
||||||
|
|
||||||
|
WebDataBinder binder = createBinder(webRequest, null, pathVarName);
|
||||||
|
initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
|
||||||
|
return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest)
|
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import java.util.HashMap;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
@@ -152,8 +151,8 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
|
|||||||
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
|
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
|
||||||
if (mapping != null) {
|
if (mapping != null) {
|
||||||
String[] mappedPaths = mapping.value();
|
String[] mappedPaths = mapping.value();
|
||||||
for (int i = 0; i < mappedPaths.length; i++) {
|
for (String mappedPath : mappedPaths) {
|
||||||
addUrlsForPath(urls, mappedPaths[i]);
|
addUrlsForPath(urls, mappedPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,4 +213,6 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ 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;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.DerivedTestBean;
|
import org.springframework.beans.DerivedTestBean;
|
||||||
import org.springframework.beans.ITestBean;
|
import org.springframework.beans.ITestBean;
|
||||||
import org.springframework.beans.TestBean;
|
import org.springframework.beans.TestBean;
|
||||||
@@ -63,6 +64,7 @@ import org.springframework.web.bind.MissingServletRequestParameterException;
|
|||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@@ -796,6 +798,29 @@ public class ServletAnnotationControllerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void uriTemplates() throws Exception {
|
||||||
|
DispatcherServlet servlet = new DispatcherServlet() {
|
||||||
|
@Override
|
||||||
|
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) throws BeansException {
|
||||||
|
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||||
|
wac.registerBeanDefinition("controller", new RootBeanDefinition(UriTemplateController.class));
|
||||||
|
wac.refresh();
|
||||||
|
return wac;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
servlet.init(new MockServletConfig());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
servlet.service(request, response);
|
||||||
|
assertEquals("test-42-21", response.getContentAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Controllers
|
||||||
|
*/
|
||||||
|
|
||||||
@RequestMapping("/myPath.do")
|
@RequestMapping("/myPath.do")
|
||||||
private static class MyController extends AbstractController {
|
private static class MyController extends AbstractController {
|
||||||
|
|
||||||
@@ -1285,7 +1310,6 @@ public class ServletAnnotationControllerTests {
|
|||||||
@Controller
|
@Controller
|
||||||
public static class MethodNotAllowedController {
|
public static class MethodNotAllowedController {
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value="/myPath.do", method = RequestMethod.DELETE)
|
@RequestMapping(value="/myPath.do", method = RequestMethod.DELETE)
|
||||||
public void delete() {
|
public void delete() {
|
||||||
}
|
}
|
||||||
@@ -1314,4 +1338,15 @@ public class ServletAnnotationControllerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public static class UriTemplateController {
|
||||||
|
|
||||||
|
@RequestMapping("/hotels/{hotel}/bookings/{booking}")
|
||||||
|
public void handle(@PathVariable("hotel") int hotel, @PathVariable int booking, HttpServletResponse response)
|
||||||
|
throws IOException {
|
||||||
|
response.getWriter().write("test-" + hotel + "-" + booking);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user