From 20f4e9023be5fb19465f2a35d786e40e57d48841 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 31 Mar 2010 14:02:27 +0000 Subject: [PATCH] UrlPathHelper cuts off trailing servlet-path slashes for root mappings (on WebSphere; SPR-7052) --- .../web/servlet/config/MvcNamespaceTests.java | 71 ++++++++++++++++--- .../config/mvc-config-view-controllers.xml | 12 ++-- .../web/util/UrlPathHelper.java | 5 +- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index d4ab4b08c9..76d1f58c4d 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -33,13 +33,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.support.FormattingConversionServiceFactoryBean; -import org.springframework.http.converter.ByteArrayHttpMessageConverter; -import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.StringHttpMessageConverter; -import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; -import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; -import org.springframework.http.converter.xml.SourceHttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; @@ -239,6 +233,7 @@ public class MvcNamespaceTests { mapping.setDefaultHandler(new TestController()); MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); HandlerExecutionChain chain = mapping.getHandler(request); assertEquals(4, chain.getInterceptors().length); @@ -253,7 +248,6 @@ public class MvcNamespaceTests { assertNotNull(adapter); request.setRequestURI("/foo"); - request.setMethod("GET"); chain = mapping2.getHandler(request); assertEquals(4, chain.getInterceptors().length); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor); @@ -262,16 +256,77 @@ public class MvcNamespaceTests { ModelAndView mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); assertNull(mv.getViewName()); - request.setRequestURI("/bar"); + request.setRequestURI("/myapp/app/bar"); + request.setContextPath("/myapp"); + request.setServletPath("/app"); + chain = mapping2.getHandler(request); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); + ModelAndView mv2 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); + assertEquals("baz", mv2.getViewName()); + + request.setRequestURI("/myapp/app/"); + request.setContextPath("/myapp"); + request.setServletPath("/app"); chain = mapping2.getHandler(request); assertEquals(4, chain.getInterceptors().length); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor); assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); + ModelAndView mv3 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); + assertEquals("root", mv3.getViewName()); + } + + /** WebSphere gives trailing servlet path slashes by default!! */ + @Test + public void testViewControllersOnWebSphere() throws Exception { + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); + reader.loadBeanDefinitions(new ClassPathResource("mvc-config-view-controllers.xml", getClass())); + assertEquals(9, appContext.getBeanDefinitionCount()); + appContext.refresh(); + + SimpleUrlHandlerMapping mapping2 = appContext.getBean(SimpleUrlHandlerMapping.class); + SimpleControllerHandlerAdapter adapter = appContext.getBean(SimpleControllerHandlerAdapter.class); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + request.setRequestURI("/myapp/app/bar"); + request.setContextPath("/myapp"); + request.setServletPath("/app/"); + HandlerExecutionChain chain = mapping2.getHandler(request); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); ModelAndView mv2 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); assertEquals("baz", mv2.getViewName()); + + request.setRequestURI("/myapp/app/"); + request.setContextPath("/myapp"); + request.setServletPath("/app/"); + chain = mapping2.getHandler(request); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); + ModelAndView mv3 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); + assertEquals("root", mv3.getViewName()); + + request.setRequestURI("/myapp/"); + request.setContextPath("/myapp"); + request.setServletPath("/"); + chain = mapping2.getHandler(request); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); + mv3 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); + assertEquals("root", mv3.getViewName()); } + @Controller public static class TestController { diff --git a/org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml b/org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml index 1d0b296d5f..4188835f46 100644 --- a/org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml +++ b/org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml @@ -5,15 +5,17 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> - + - + - + + + - - + + diff --git a/org.springframework.web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/org.springframework.web/src/main/java/org/springframework/web/util/UrlPathHelper.java index 611443056e..22a5e7dcc1 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/org.springframework.web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -18,7 +18,6 @@ package org.springframework.web.util; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; - import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; @@ -226,6 +225,10 @@ public class UrlPathHelper { if (servletPath == null) { servletPath = request.getServletPath(); } + if (servletPath.length() > 1 && servletPath.endsWith("/")) { + // Probably on WebSphere: removing trailing slash... + servletPath = servletPath.substring(0, servletPath.length() - 1); + } return servletPath; }