();
// Single-purpose return value types
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/UriComponentsBuilderMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/UriComponentsBuilderMethodArgumentResolver.java
new file mode 100644
index 00000000000..7045c0f1ef2
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/UriComponentsBuilderMethodArgumentResolver.java
@@ -0,0 +1,53 @@
+/*
+ * 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.annotation.support;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * Resolvers argument values of type {@link UriComponentsBuilder}.
+ *
+ * The returned instance is initialized via
+ * {@link ServletUriComponentsBuilder#fromServletMapping(HttpServletRequest)}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.1
+ */
+public class UriComponentsBuilderMethodArgumentResolver implements HandlerMethodArgumentResolver {
+
+ public boolean supportsParameter(MethodParameter parameter) {
+ return UriComponentsBuilder.class.isAssignableFrom(parameter.getParameterType());
+ }
+
+ public Object resolveArgument(MethodParameter parameter,
+ ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest,
+ WebDataBinderFactory binderFactory) throws Exception {
+
+ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+ return ServletUriComponentsBuilder.fromServletMapping(request);
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java
index ee2e052b3da..33dd364f29c 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import java.awt.Color;
import java.lang.reflect.Method;
+import java.net.URI;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -85,6 +86,7 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
+import org.springframework.web.util.UriComponentsBuilder;
/**
* A test fixture with a controller with all supported method signature styles
@@ -142,7 +144,7 @@ public class RequestMappingHandlerAdapterIntegrationTests {
Class>[] parameterTypes = new Class>[] { int.class, String.class, String.class, String.class, Map.class,
Date.class, Map.class, String.class, String.class, TestBean.class, Errors.class, TestBean.class,
Color.class, HttpServletRequest.class, HttpServletResponse.class, User.class, OtherUser.class,
- Model.class };
+ Model.class, UriComponentsBuilder.class };
String datePattern = "yyyy.MM.dd";
String formattedDate = "2011.03.16";
@@ -159,6 +161,7 @@ public class RequestMappingHandlerAdapterIntegrationTests {
request.setContent("Hello World".getBytes("UTF-8"));
request.setUserPrincipal(new User());
request.setContextPath("/contextPath");
+ request.setServletPath("/main");
System.setProperty("systemHeader", "systemHeaderValue");
Map uriTemplateVars = new HashMap();
uriTemplateVars.put("pathvar", "pathvarValue");
@@ -206,6 +209,8 @@ public class RequestMappingHandlerAdapterIntegrationTests {
assertTrue(model.get("customArg") instanceof Color);
assertEquals(User.class, model.get("user").getClass());
assertEquals(OtherUser.class, model.get("otherUser").getClass());
+
+ assertEquals(new URI("http://localhost/contextPath/main/path"), model.get("url"));
}
@Test
@@ -309,13 +314,15 @@ public class RequestMappingHandlerAdapterIntegrationTests {
HttpServletResponse response,
User user,
@ModelAttribute OtherUser otherUser,
- Model model) throws Exception {
+ Model model,
+ UriComponentsBuilder builder) throws Exception {
model.addAttribute("cookie", cookie).addAttribute("pathvar", pathvar).addAttribute("header", header)
.addAttribute("systemHeader", systemHeader).addAttribute("headerMap", headerMap)
.addAttribute("dateParam", dateParam).addAttribute("paramMap", paramMap)
.addAttribute("paramByConvention", paramByConvention).addAttribute("value", value)
- .addAttribute("customArg", customArg).addAttribute(user);
+ .addAttribute("customArg", customArg).addAttribute(user)
+ .addAttribute("url", builder.path("/path").build().toUri());
assertNotNull(request);
assertNotNull(response);
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/UriComponentsBuilderMethodArgumentResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/UriComponentsBuilderMethodArgumentResolverTests.java
new file mode 100644
index 00000000000..3ebe78307ad
--- /dev/null
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/UriComponentsBuilderMethodArgumentResolverTests.java
@@ -0,0 +1,89 @@
+/*
+ * 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.annotation.support;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.MethodParameter;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.web.context.request.ServletWebRequest;
+import org.springframework.web.method.support.ModelAndViewContainer;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * Test fixture with {@link UriComponentsBuilderMethodArgumentResolver}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class UriComponentsBuilderMethodArgumentResolverTests {
+
+ private UriComponentsBuilderMethodArgumentResolver resolver;
+
+ private MethodParameter builderParam;
+
+ private MethodParameter servletBuilderParam;
+
+ private MethodParameter intParam;
+
+ private ServletWebRequest webRequest;
+
+ private MockHttpServletRequest servletRequest;
+
+ @Before
+ public void setUp() throws Exception {
+ this.resolver = new UriComponentsBuilderMethodArgumentResolver();
+ Method method = this.getClass().getDeclaredMethod("handle", UriComponentsBuilder.class, ServletUriComponentsBuilder.class, int.class);
+ this.builderParam = new MethodParameter(method, 0);
+ this.servletBuilderParam = new MethodParameter(method, 1);
+ this.intParam = new MethodParameter(method, 2);
+ this.servletRequest = new MockHttpServletRequest();
+ this.webRequest = new ServletWebRequest(this.servletRequest);
+ }
+
+ @Test
+ public void supportsParameter() throws Exception {
+ assertTrue(this.resolver.supportsParameter(this.builderParam));
+ assertTrue(this.resolver.supportsParameter(this.servletBuilderParam));
+ assertFalse(this.resolver.supportsParameter(this.intParam));
+ }
+
+ @Test
+ public void resolveArgument() throws Exception {
+ this.servletRequest.setContextPath("/myapp");
+ this.servletRequest.setServletPath("/main");
+ this.servletRequest.setPathInfo("/accounts");
+
+ Object actual = this.resolver.resolveArgument(this.builderParam, new ModelAndViewContainer(), this.webRequest, null);
+
+ assertNotNull(actual);
+ assertEquals(ServletUriComponentsBuilder.class, actual.getClass());
+ assertEquals("http://localhost/myapp/main", ((ServletUriComponentsBuilder) actual).build().toUriString());
+ }
+
+
+ void handle(UriComponentsBuilder builder, ServletUriComponentsBuilder servletBuilder, int value) {
+ }
+
+}
\ No newline at end of file
diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java
index 7fe0deaff54..7a4ff06b2e5 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java
@@ -130,6 +130,9 @@ import java.lang.annotation.Target;
* for marking form processing as complete (triggering the cleanup of session
* attributes that have been indicated by the {@link SessionAttributes} annotation
* at the handler type level).
+ * {@link org.springframework.web.util.UriComponentsBuilder} a builder for
+ * preparing a URL relative to the current request's host, port, scheme, context
+ * path, and the literal part of the servlet mapping.
*
*
* The following return types are supported for handler methods:
diff --git a/org.springframework.web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/org.springframework.web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java
index 7a307290cc7..f4ed1f252e1 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java
@@ -20,6 +20,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -221,6 +222,30 @@ public class UriComponentsBuilder {
return new UriComponents(scheme, userInfo, host, port, pathBuilder.build(), queryParams, fragment, encoded, true);
}
+ /**
+ * Builds a {@code UriComponents} instance and replaces URI template variables
+ * with the values from a map. This is a shortcut method, which combines
+ * calls to {@link #build()} and then {@link UriComponents#expand(Map)}.
+ *
+ * @param uriVariables the map of URI variables
+ * @return the URI components with expanded values
+ */
+ public UriComponents buildAndExpand(Map uriVariables) {
+ return build(false).expand(uriVariables);
+ }
+
+ /**
+ * Builds a {@code UriComponents} instance and replaces URI template variables
+ * with the values from an array. This is a shortcut method, which combines
+ * calls to {@link #build()} and then {@link UriComponents#expand(Object...)}.
+ *
+ * @param uriVariableValues URI variable values
+ * @return the URI components with expanded values
+ */
+ public UriComponents buildAndExpand(Object... uriVariableValues) {
+ return build(false).expand(uriVariableValues);
+ }
+
// URI components methods
/**
diff --git a/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java
index 0890729c23a..303bcd2d157 100644
--- a/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java
+++ b/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java
@@ -16,17 +16,19 @@
package org.springframework.web.util;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import org.junit.Test;
-
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
-import static org.junit.Assert.*;
-
/** @author Arjen Poutsma */
public class UriComponentsBuilderTests {
@@ -227,7 +229,6 @@ public class UriComponentsBuilderTests {
assertEquals(expectedQueryParams, result.getQueryParams());
}
-
@Test
public void replaceQueryParam() {
UriComponentsBuilder builder = UriComponentsBuilder.newInstance().queryParam("baz", "qux", 42);
@@ -243,4 +244,15 @@ public class UriComponentsBuilderTests {
assertNull("Query param should have been deleted", result.getQuery());
}
+ @Test
+ public void buildAndExpand() {
+ UriComponents result = UriComponentsBuilder.fromPath("/{foo}").buildAndExpand("fooValue");
+ assertEquals("/fooValue", result.toUriString());
+
+ Map values = new HashMap();
+ values.put("foo", "fooValue");
+ values.put("bar", "barValue");
+ result = UriComponentsBuilder.fromPath("/{foo}/{bar}").buildAndExpand(values);
+ assertEquals("/fooValue/barValue", result.toUriString());
+ }
}
diff --git a/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsTests.java b/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsTests.java
index 596a89959f6..00049b00c8b 100644
--- a/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsTests.java
+++ b/org.springframework.web/src/test/java/org/springframework/web/util/UriComponentsTests.java
@@ -62,7 +62,6 @@ public class UriComponentsTests {
@Test(expected = IllegalArgumentException.class)
public void invalidCharacters() {
UriComponentsBuilder.fromPath("/{foo}").build(true);
-
}
@Test(expected = IllegalArgumentException.class)
diff --git a/spring-framework-reference/src/mvc.xml b/spring-framework-reference/src/mvc.xml
index 1ddf737e170..0e0aead14d7 100644
--- a/spring-framework-reference/src/mvc.xml
+++ b/spring-framework-reference/src/mvc.xml
@@ -1199,6 +1199,13 @@ public class RelativePathUriTemplateController {
indicated by the @SessionAttributes
annotation at the handler type level.
+
+
+ org.springframework.web.util.UriComponentsBuilder
+ a builder for preparing a URL relative to the current request's
+ host, port, scheme, context path, and the literal part of the
+ servlet mapping.
+
The Errors or