SPR-8020 Support UriComponentsBuilder as a controller method argument.

The UriComponentsBuilder instance passed into the method is initialized
with current request information including host, scheme, port, context
path, and the servlet mapping's literal part.

Also added shortcut methods to buildAndExpand in UriComponentsBuilder.
This commit is contained in:
Rossen Stoyanchev
2011-11-18 14:36:44 +00:00
parent e4fada56ab
commit 60ee0bb8f4
10 changed files with 210 additions and 12 deletions

View File

@@ -89,6 +89,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletCook
import org.springframework.web.servlet.mvc.method.annotation.support.ServletModelAttributeMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.UriComponentsBuilderMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.support.ViewNameMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@@ -425,7 +426,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
*/
protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
@@ -449,6 +450,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
@@ -466,7 +468,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* Return the list of argument resolvers to use for {@code @InitBinder}
* methods including built-in and custom resolvers.
*/
protected List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
@@ -494,7 +496,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* Return the list of return value handlers to use including built-in and
* custom handlers provided via {@link #setReturnValueHandlers}.
*/
protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
// Single-purpose return value types

View File

@@ -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}.
*
* <p>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);
}
}

View File

@@ -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<String, String> uriTemplateVars = new HashMap<String, String>();
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);

View File

@@ -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) {
}
}