diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index 45a781c2a9..3d4f08da01 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -280,6 +280,7 @@ public class HandlerMethodInvoker { String paramName = null; boolean paramRequired = false; String paramDefaultValue = null; + String pathVarName = null; Object[] paramAnns = methodParam.getParameterAnnotations(); for (Object paramAnn : paramAnns) { @@ -294,9 +295,13 @@ public class HandlerMethodInvoker { throw new IllegalStateException( "@ModelAttribute is not supported on @InitBinder methods: " + initBinderMethod); } + else if (PathVariable.class.isInstance(paramAnn)) { + PathVariable pathVar = (PathVariable) paramAnn; + pathVarName = pathVar.value(); + } } - if (paramName == null) { + if (paramName == null && pathVarName == null) { Object argValue = resolveCommonArgument(methodParam, webRequest); if (argValue != WebArgumentResolver.UNRESOLVED) { initBinderArgs[i] = argValue; @@ -319,6 +324,8 @@ public class HandlerMethodInvoker { if (paramName != null) { initBinderArgs[i] = resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null); + } else if (pathVarName != null) { + initBinderArgs[i] = resolvePathVariable(pathVarName, methodParam, webRequest, null); } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index bcf535702f..d234cf1efa 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -40,7 +40,6 @@ import org.junit.Test; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.support.DefaultPointcutAdvisor; -import org.springframework.beans.BeansException; import org.springframework.beans.DerivedTestBean; import org.springframework.beans.ITestBean; import org.springframework.beans.TestBean; @@ -64,7 +63,6 @@ import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; 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.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -798,24 +796,6 @@ 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 @@ -1338,15 +1318,5 @@ 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); - } - - } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java new file mode 100644 index 0000000000..dcbad780a1 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java @@ -0,0 +1,112 @@ +package org.springframework.web.servlet.mvc.annotation; + +import java.io.IOException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletConfig; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.GenericWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +/** @author Arjen Poutsma */ +public class UriTemplateServletAnnotationControllerTests { + + private DispatcherServlet servlet; + + @Test + public void simple() throws Exception { + initServlet(SimpleUriTemplateController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("test-42-21", response.getContentAsString()); + } + + @Test + public void binding() throws Exception { + initServlet(BindingUriTemplateController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-11-18"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("test-42", response.getContentAsString()); + } + + private void initServlet(final Class controllerclass) throws ServletException { + servlet = new DispatcherServlet() { + @Override + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) + throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerclass)); + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + } + + @Controller + public static class SimpleUriTemplateController { + + @RequestMapping("/hotels/{hotel}/bookings/{booking}") + public void handle(@PathVariable("hotel") String hotel, @PathVariable int booking, Writer writer) throws IOException { + assertEquals("Invalid path variable value", "42", hotel); + assertEquals("Invalid path variable value", 21, booking); + writer.write("test-" + hotel + "-" + booking); + } + + } + + @Controller + public static class BindingUriTemplateController { + + @InitBinder + public void initBinder(WebDataBinder binder, @PathVariable("hotel") String hotel) { + assertEquals("Invalid path variable value", "42", hotel); + binder.initBeanPropertyAccess(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + + @RequestMapping("/hotels/{hotel}/dates/{date}") + public void handle(@PathVariable("hotel") String hotel, @PathVariable Date date, Writer writer) throws IOException { + assertEquals("Invalid path variable value", "42", hotel); + assertEquals("Invalid path variable value", new Date(108, 10, 18), date); + writer.write("test-" + hotel); + } + + } + + @Controller + @RequestMapping("/hotels/{hotel}/**") + public static class RelativePathUriTemplateController { + + @RequestMapping("/bookings/{booking}") + public void handle(@PathVariable("hotel") int hotel, @PathVariable int booking, HttpServletResponse response) + throws IOException { + response.getWriter().write("test-" + hotel + "-" + booking); + } + + } + +}