diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java
index 017603a91a..69cced3500 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java
@@ -35,6 +35,8 @@ public class MvcNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
+ registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
+ registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles", new TilesBeanDefinitionParser());
registerBeanDefinitionParser("freemarker", new FreeMarkerBeanDefinitionParser());
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java
index 1161c85cdd..a145684a44 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java
@@ -19,19 +19,32 @@ package org.springframework.web.servlet.config;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
+import org.springframework.web.servlet.view.RedirectView;
import org.w3c.dom.Element;
/**
- * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
- * {@code view-controller} element to register a {@link ParameterizableViewController}.
- * Will also register a {@link SimpleUrlHandlerMapping} for view controllers.
+ * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that
+ * parses the following MVC namespace elements:
+ *
+ * - {@code }
+ *
- {@code }
+ *
- {@code }
+ *
+ *
+ * All elements result in the registration of a
+ * {@link org.springframework.web.servlet.mvc.ParameterizableViewController
+ * ParameterizableViewController} with all controllers mapped using in a single
+ * {@link org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
+ * SimpleUrlHandlerMapping}.
*
* @author Keith Donald
* @author Christian Dupuis
@@ -50,7 +63,7 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
Object source = parserContext.extractSource(element);
// Register SimpleUrlHandlerMapping for view controllers
- BeanDefinition handlerMapping = registerHandlerMapping(parserContext, source);
+ BeanDefinition hm = registerHandlerMapping(parserContext, source);
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
@@ -58,16 +71,41 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
// Create view controller bean definition
RootBeanDefinition controller = new RootBeanDefinition(ParameterizableViewController.class);
controller.setSource(source);
- if (element.hasAttribute("view-name")) {
- controller.getPropertyValues().add("viewName", element.getAttribute("view-name"));
+
+ HttpStatus statusCode = null;
+ if (element.hasAttribute("status-code")) {
+ int statusValue = Integer.valueOf(element.getAttribute("status-code"));
+ statusCode = HttpStatus.valueOf(statusValue);
}
+
+ String name = element.getLocalName();
+ if (name.equals("view-controller")) {
+ if (element.hasAttribute("view-name")) {
+ controller.getPropertyValues().add("viewName", element.getAttribute("view-name"));
+ }
+ if (statusCode != null) {
+ controller.getPropertyValues().add("statusCode", statusCode);
+ }
+ }
+ else if (name.equals("redirect-view-controller")) {
+ controller.getPropertyValues().add("view", getRedirectView(element, statusCode, source));
+ }
+ else if (name.equals("status-controller")) {
+ controller.getPropertyValues().add("statusCode", statusCode);
+ controller.getPropertyValues().add("statusOnly", true);
+ }
+ else {
+ // Should never happen...
+ throw new IllegalStateException("Unexpected tag name: " + name);
+ }
+
Map urlMap;
- if (handlerMapping.getPropertyValues().contains("urlMap")) {
- urlMap = (Map) handlerMapping.getPropertyValues().getPropertyValue("urlMap").getValue();
+ if (hm.getPropertyValues().contains("urlMap")) {
+ urlMap = (Map) hm.getPropertyValues().getPropertyValue("urlMap").getValue();
}
else {
urlMap = new ManagedMap();
- handlerMapping.getPropertyValues().add("urlMap", urlMap);
+ hm.getPropertyValues().add("urlMap", urlMap);
}
urlMap.put(element.getAttribute("path"), controller);
@@ -91,4 +129,21 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
return beanDef;
}
+ private RootBeanDefinition getRedirectView(Element element, HttpStatus status, Object source) {
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addIndexedArgumentValue(0, element.getAttribute("redirect-url"));
+ RootBeanDefinition redirectView = new RootBeanDefinition(RedirectView.class, cavs, null);
+ redirectView.setSource(source);
+ if (status != null) {
+ redirectView.getPropertyValues().add("statusCode", status);
+ }
+ if (element.hasAttribute("context-relative")) {
+ redirectView.getPropertyValues().add("contextRelative", element.getAttribute("context-relative"));
+ }
+ if (element.hasAttribute("keep-query-params")) {
+ redirectView.getPropertyValues().add("propagateQueryParams", element.getAttribute("keep-query-params"));
+ }
+ return redirectView;
+ }
+
}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/RedirectViewControllerRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/RedirectViewControllerRegistration.java
new file mode 100644
index 0000000000..6bf1a8163c
--- /dev/null
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/RedirectViewControllerRegistration.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2002-2014 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.config.annotation;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.util.Assert;
+import org.springframework.web.servlet.mvc.ParameterizableViewController;
+import org.springframework.web.servlet.view.RedirectView;
+
+/**
+ * Assist with the registration of a single redirect view controller.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.1
+ */
+public class RedirectViewControllerRegistration {
+
+ private final String urlPath;
+
+ private final RedirectView redirectView;
+
+ private final ParameterizableViewController controller = new ParameterizableViewController();
+
+
+ public RedirectViewControllerRegistration(String urlPath, String redirectUrl) {
+ Assert.notNull(urlPath, "'urlPath' is required.");
+ Assert.notNull(redirectUrl, "'redirectUrl' is required.");
+ this.urlPath = urlPath;
+ this.redirectView = new RedirectView(redirectUrl);
+ this.redirectView.setContextRelative(true);
+ this.controller.setView(this.redirectView);
+ }
+
+
+ /**
+ * Set the specific redirect 3xx status code to use.
+ *
+ * If not set, {@link org.springframework.web.servlet.view.RedirectView}
+ * will select {@code HttpStatus.MOVED_TEMPORARILY (302)} by default.
+ */
+ public RedirectViewControllerRegistration setStatusCode(HttpStatus statusCode) {
+ Assert.isTrue(statusCode.is3xxRedirection(), "Not a redirect status code.");
+ this.redirectView.setStatusCode(statusCode);
+ return this;
+ }
+
+ /**
+ * Whether to interpret a given redirect URL that starts with a slash ("/")
+ * as relative to the current ServletContext, i.e. as relative to the web
+ * application root.
+ *
+ *
Default is {@code true}.
+ */
+ public RedirectViewControllerRegistration setContextRelative(boolean contextRelative) {
+ this.redirectView.setContextRelative(contextRelative);
+ return this;
+ }
+
+ /**
+ * Whether to propagate the query parameters of the current request through
+ * to the target redirect URL.
+ *
+ *
Default is {@code false}.
+ */
+ public RedirectViewControllerRegistration setKeepQueryParams(boolean propagate) {
+ this.redirectView.setPropagateQueryParams(propagate);
+ return this;
+ }
+
+
+ protected String getUrlPath() {
+ return this.urlPath;
+ }
+
+ protected ParameterizableViewController getViewController() {
+ return this.controller;
+ }
+
+}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistration.java
index 5b1f069441..eb486d9582 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistration.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistration.java
@@ -16,6 +16,7 @@
package org.springframework.web.servlet.config.annotation;
+import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.web.servlet.RequestToViewNameTranslator;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
@@ -31,12 +32,9 @@ public class ViewControllerRegistration {
private final String urlPath;
- private String viewName;
+ private final ParameterizableViewController controller = new ParameterizableViewController();
- /**
- * Creates a registration for the given URL path (or path pattern).
- */
public ViewControllerRegistration(String urlPath) {
Assert.notNull(urlPath, "'urlPath' is required.");
this.urlPath = urlPath;
@@ -44,17 +42,28 @@ public class ViewControllerRegistration {
/**
- * Set the view name to return.
+ * Set the status code to set on the response. Optional.
*
- *
If not specified, the view controller returns {@code null} as the view
- * name in which case the configured {@link RequestToViewNameTranslator}
- * selects the view. In effect {@code DefaultRequestToViewNameTranslator}
- * translates "/foo/bar" to "foo/bar".
+ *
If not set the response status will be 200 (OK).
+ */
+ public ViewControllerRegistration setStatusCode(HttpStatus statusCode) {
+ this.controller.setStatusCode(statusCode);
+ return this;
+ }
+
+ /**
+ * Set the view name to return. Optional.
+ *
+ *
If not specified, the view controller will return {@code null} as the
+ * view name in which case the configured {@link RequestToViewNameTranslator}
+ * will select the view name. The {@code DefaultRequestToViewNameTranslator}
+ * for example translates "/foo/bar" to "foo/bar".
*
* @see org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
*/
- public void setViewName(String viewName) {
- this.viewName = viewName;
+ public ViewControllerRegistration setViewName(String viewName) {
+ this.controller.setViewName(viewName);
+ return this;
}
@@ -62,10 +71,8 @@ public class ViewControllerRegistration {
return this.urlPath;
}
- protected Object getViewController() {
- ParameterizableViewController controller = new ParameterizableViewController();
- controller.setViewName(this.viewName);
- return controller;
+ protected ParameterizableViewController getViewController() {
+ return this.controller;
}
}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java
index 11535f7c00..8b7fc6073c 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java
@@ -21,14 +21,13 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import org.springframework.web.servlet.HandlerMapping;
+import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
/**
- * Enables the registration of view controllers that have no logic other than to
- * return the view name they're configured with. This is an alternative to
- * writing a controller manually to do the same.
+ * Assists with the registration of simple automated controllers pre-configured
+ * with status code and/or a view.
*
* @author Rossen Stoyanchev
* @author Keith Donald
@@ -36,13 +35,17 @@ import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
*/
public class ViewControllerRegistry {
- private final List registrations = new ArrayList();
+ private final List registrations = new ArrayList(4);
+
+ private final List redirectRegistrations =
+ new ArrayList(10);
private int order = 1;
/**
- * Register a view controller mapped to the given URL path or URL path pattern.
+ * Map a view controller to the given URL path (or pattern) in order to render
+ * a response with a pre-configured status code and view.
*/
public ViewControllerRegistration addViewController(String urlPath) {
ViewControllerRegistration registration = new ViewControllerRegistration(urlPath);
@@ -50,6 +53,30 @@ public class ViewControllerRegistry {
return registration;
}
+ /**
+ * Map a view controller to the given URL path (or pattern) in order to redirect
+ * to another URL. By default the redirect URL is expected to be relative to
+ * the current ServletContext, i.e. as relative to the web application root.
+ * @since 4.1
+ */
+ public RedirectViewControllerRegistration addRedirectViewController(String urlPath, String redirectUrl) {
+ RedirectViewControllerRegistration registration = new RedirectViewControllerRegistration(urlPath, redirectUrl);
+ this.redirectRegistrations.add(registration);
+ return registration;
+ }
+
+ /**
+ * Map a simple controller to the given URL path (or pattern) in order to
+ * set the response status to the given code without rendering a body.
+ * @since 4.1
+ */
+ public void addStatusController(String urlPath, HttpStatus statusCode) {
+ ViewControllerRegistration registration = new ViewControllerRegistration(urlPath);
+ registration.setStatusCode(statusCode);
+ registration.getViewController().setStatusOnly(true);
+ this.registrations.add(registration);
+ }
+
/**
* Specify the order to use for the {@code HandlerMapping} used to map view
* controllers relative to other handler mappings configured in Spring MVC.
@@ -66,13 +93,16 @@ public class ViewControllerRegistry {
* controller mappings, or {@code null} for no registrations.
*/
protected AbstractHandlerMapping getHandlerMapping() {
- if (this.registrations.isEmpty()) {
+ if (this.registrations.isEmpty() && this.redirectRegistrations.isEmpty()) {
return null;
}
Map urlMap = new LinkedHashMap();
for (ViewControllerRegistration registration : this.registrations) {
urlMap.put(registration.getUrlPath(), registration.getViewController());
}
+ for (RedirectViewControllerRegistration registration : this.redirectRegistrations) {
+ urlMap.put(registration.getUrlPath(), registration.getViewController());
+ }
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(this.order);
handlerMapping.setUrlMap(urlMap);
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
index 7f6021581f..3c4c120104 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
@@ -133,8 +133,11 @@ public interface WebMvcConfigurer {
MessageCodesResolver getMessageCodesResolver();
/**
- * Add view controllers to create a direct mapping between a URL path and
- * view name without the need for a controller in between.
+ * Configure simple automated controllers pre-configured with the response
+ * status code and/or a view to render the response body. This is useful in
+ * cases where there is no need for custom controller logic -- e.g. render a
+ * home page, perform simple site URL redirects, return a 404 status with
+ * HTML content, a 204 with no content, and more.
*/
void addViewControllers(ViewControllerRegistry registry);
diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd
index 6d2758ae77..be54f0ca3a 100644
--- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd
+++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd
@@ -574,27 +574,115 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java
index d08724a344..0a9d7393e4 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java
@@ -37,6 +37,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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.mock.web.test.MockHttpServletRequest;
@@ -54,6 +55,7 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.accept.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
@@ -61,6 +63,7 @@ import org.springframework.web.context.request.async.CallableProcessingIntercept
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptorAdapter;
import org.springframework.web.context.support.GenericWebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.CompositeUriComponentsContributor;
import org.springframework.web.method.support.InvocableHandlerMethod;
@@ -118,10 +121,14 @@ public class MvcNamespaceTests {
@Before
public void setUp() throws Exception {
+ TestMockServletContext servletContext = new TestMockServletContext();
appContext = new GenericWebApplicationContext();
- appContext.setServletContext(new TestMockServletContext());
+ appContext.setServletContext(servletContext);
LocaleContextHolder.setLocale(Locale.US);
+ String attributeName = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
+ appContext.getServletContext().setAttribute(attributeName, appContext);
+
handler = new TestController();
Method method = TestController.class.getMethod("testBind", Date.class, TestBean.class, BindingResult.class);
handlerMethod = new InvocableHandlerMethod(handler, method);
@@ -443,7 +450,7 @@ public class MvcNamespaceTests {
SimpleControllerHandlerAdapter adapter = appContext.getBean(SimpleControllerHandlerAdapter.class);
assertNotNull(adapter);
- request.setRequestURI("/foo");
+ request = new MockHttpServletRequest("GET", "/foo");
chain = mapping2.getHandler(request);
assertEquals(4, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
@@ -452,7 +459,7 @@ public class MvcNamespaceTests {
ModelAndView mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler());
assertNull(mv.getViewName());
- request.setRequestURI("/myapp/app/bar");
+ request = new MockHttpServletRequest("GET", "/myapp/app/bar");
request.setContextPath("/myapp");
request.setServletPath("/app");
chain = mapping2.getHandler(request);
@@ -460,10 +467,10 @@ public class MvcNamespaceTests {
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());
+ mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler());
+ assertEquals("baz", mv.getViewName());
- request.setRequestURI("/myapp/app/");
+ request = new MockHttpServletRequest("GET", "/myapp/app/");
request.setContextPath("/myapp");
request.setServletPath("/app");
chain = mapping2.getHandler(request);
@@ -471,8 +478,29 @@ public class MvcNamespaceTests {
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());
+ mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler());
+ assertEquals("root", mv.getViewName());
+
+ request = new MockHttpServletRequest("GET", "/myapp/app/old");
+ request.setContextPath("/myapp");
+ request.setServletPath("/app");
+ request.setQueryString("a=b");
+ chain = mapping2.getHandler(request);
+ mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler());
+ assertNotNull(mv.getView());
+ assertEquals(RedirectView.class, mv.getView().getClass());
+ RedirectView redirectView = (RedirectView) mv.getView();
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ redirectView.render(Collections.emptyMap(), request, response);
+ assertEquals("/new?a=b", response.getRedirectedUrl());
+ assertEquals(308, response.getStatus());
+
+ request = new MockHttpServletRequest("GET", "/bad");
+ chain = mapping2.getHandler(request);
+ response = new MockHttpServletResponse();
+ mv = adapter.handle(request, response, chain.getHandler());
+ assertNull(mv);
+ assertEquals(404, response.getStatus());
}
/** WebSphere gives trailing servlet path slashes by default!! */
@@ -524,7 +552,13 @@ public class MvcNamespaceTests {
public void testViewControllersDefaultConfig() {
loadBeanDefinitions("mvc-config-view-controllers-minimal.xml", 6);
- BeanNameUrlHandlerMapping beanNameMapping = appContext.getBean(BeanNameUrlHandlerMapping.class);
+ SimpleUrlHandlerMapping hm = this.appContext.getBean(SimpleUrlHandlerMapping.class);
+ assertNotNull(hm);
+ assertNotNull(hm.getUrlMap().get("/path"));
+ assertNotNull(hm.getUrlMap().get("/old"));
+ assertNotNull(hm.getUrlMap().get("/bad"));
+
+ BeanNameUrlHandlerMapping beanNameMapping = this.appContext.getBean(BeanNameUrlHandlerMapping.class);
assertNotNull(beanNameMapping);
assertEquals(2, beanNameMapping.getOrder());
}
@@ -683,8 +717,8 @@ public class MvcNamespaceTests {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext);
ClassPathResource resource = new ClassPathResource(fileName, AnnotationDrivenBeanDefinitionParserTests.class);
reader.loadBeanDefinitions(resource);
- assertEquals("Bean names: " + Arrays.toString(this.appContext.getBeanDefinitionNames()),
- expectedBeanCount, appContext.getBeanDefinitionCount());
+ String names = Arrays.toString(this.appContext.getBeanDefinitionNames());
+ assertEquals("Bean names: " + names, expectedBeanCount, appContext.getBeanDefinitionCount());
appContext.refresh();
}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistryTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistryTests.java
index cb76073a98..ebc7736f3f 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistryTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistryTests.java
@@ -17,15 +17,22 @@
package org.springframework.web.servlet.config.annotation;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.mock.web.test.MockHttpServletRequest;
+import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
+import org.springframework.web.servlet.view.RedirectView;
/**
* Test fixture with a {@link ViewControllerRegistry}.
@@ -36,10 +43,16 @@ public class ViewControllerRegistryTests {
private ViewControllerRegistry registry;
+ private MockHttpServletRequest request;
+
+ private MockHttpServletResponse response;
+
@Before
public void setUp() {
this.registry = new ViewControllerRegistry();
+ this.request = new MockHttpServletRequest("GET", "/");
+ this.response = new MockHttpServletResponse();
}
@Test
@@ -50,21 +63,59 @@ public class ViewControllerRegistryTests {
@Test
public void addViewController() {
this.registry.addViewController("/path").setViewName("viewName");
- Map urlMap = getHandlerMapping().getUrlMap();
- ParameterizableViewController controller = (ParameterizableViewController) urlMap.get("/path");
- assertNotNull(controller);
+ ParameterizableViewController controller = getController("/path");
assertEquals("viewName", controller.getViewName());
+ assertNull(controller.getStatusCode());
+ assertFalse(controller.isStatusOnly());
}
@Test
public void addViewControllerWithDefaultViewName() {
this.registry.addViewController("/path");
- Map urlMap = getHandlerMapping().getUrlMap();
- ParameterizableViewController controller = (ParameterizableViewController) urlMap.get("/path");
- assertNotNull(controller);
+ ParameterizableViewController controller = getController("/path");
assertNull(controller.getViewName());
+ assertNull(controller.getStatusCode());
+ assertFalse(controller.isStatusOnly());
}
+ @Test
+ public void addRedirectViewController() throws Exception {
+ this.registry.addRedirectViewController("/path", "/redirectTo");
+ RedirectView redirectView = getRedirectView("/path");
+ this.request.setQueryString("a=b");
+ this.request.setContextPath("/context");
+ redirectView.render(Collections.emptyMap(), this.request, this.response);
+
+ assertEquals(302, this.response.getStatus());
+ assertEquals("/context/redirectTo", this.response.getRedirectedUrl());
+ }
+
+ @Test
+ public void addRedirectViewControllerWithCustomSettings() throws Exception {
+ this.registry.addRedirectViewController("/path", "/redirectTo")
+ .setContextRelative(false)
+ .setKeepQueryParams(true)
+ .setStatusCode(HttpStatus.PERMANENT_REDIRECT);
+
+ RedirectView redirectView = getRedirectView("/path");
+ this.request.setQueryString("a=b");
+ this.request.setContextPath("/context");
+ redirectView.render(Collections.emptyMap(), this.request, this.response);
+
+ assertEquals(308, this.response.getStatus());
+ assertEquals("/redirectTo?a=b", response.getRedirectedUrl());
+ }
+
+ @Test
+ public void addStatusController() {
+ this.registry.addStatusController("/path", HttpStatus.NOT_FOUND);
+ ParameterizableViewController controller = getController("/path");
+ assertNull(controller.getViewName());
+ assertEquals(HttpStatus.NOT_FOUND, controller.getStatusCode());
+ assertTrue(controller.isStatusOnly());
+ }
+
+
@Test
public void order() {
this.registry.addViewController("/path");
@@ -76,9 +127,24 @@ public class ViewControllerRegistryTests {
assertEquals(2, handlerMapping.getOrder());
}
+ private ParameterizableViewController getController(String path) {
+ Map urlMap = getHandlerMapping().getUrlMap();
+ ParameterizableViewController controller = (ParameterizableViewController) urlMap.get(path);
+ assertNotNull(controller);
+ return controller;
+ }
private SimpleUrlHandlerMapping getHandlerMapping() {
return (SimpleUrlHandlerMapping) this.registry.getHandlerMapping();
}
+ private RedirectView getRedirectView(String path) {
+ ParameterizableViewController controller = getController("/path");
+ assertNull(controller.getViewName());
+ assertNotNull(controller.getView());
+ assertEquals(RedirectView.class, controller.getView().getClass());
+ return (RedirectView) controller.getView();
+ }
+
+
}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java
index 17283da269..7eca7a9281 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java
@@ -26,6 +26,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.core.Ordered;
+import org.springframework.http.HttpStatus;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.FileSystemResourceLoader;
@@ -121,8 +122,12 @@ public class WebMvcConfigurationSupportExtensionTests {
assertEquals(1, handlerMapping.getOrder());
assertEquals(TestPathHelper.class, handlerMapping.getUrlPathHelper().getClass());
assertEquals(TestPathMatcher.class, handlerMapping.getPathMatcher().getClass());
- HandlerExecutionChain handler = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/path"));
- assertNotNull(handler.getHandler());
+ chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/path"));
+ assertNotNull(chain.getHandler());
+ chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/bad"));
+ assertNotNull(chain.getHandler());
+ chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/old"));
+ assertNotNull(chain.getHandler());
handlerMapping = (AbstractHandlerMapping) this.config.resourceHandlerMapping();
handlerMapping.setApplicationContext(this.context);
@@ -130,15 +135,15 @@ public class WebMvcConfigurationSupportExtensionTests {
assertEquals(Integer.MAX_VALUE - 1, handlerMapping.getOrder());
assertEquals(TestPathHelper.class, handlerMapping.getUrlPathHelper().getClass());
assertEquals(TestPathMatcher.class, handlerMapping.getPathMatcher().getClass());
- handler = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/resources/foo.gif"));
- assertNotNull(handler.getHandler());
+ chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/resources/foo.gif"));
+ assertNotNull(chain.getHandler());
handlerMapping = (AbstractHandlerMapping) this.config.defaultServletHandlerMapping();
handlerMapping.setApplicationContext(this.context);
assertNotNull(handlerMapping);
assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
- handler = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/anyPath"));
- assertNotNull(handler.getHandler());
+ chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/anyPath"));
+ assertNotNull(chain.getHandler());
}
@SuppressWarnings("unchecked")
@@ -347,7 +352,9 @@ public class WebMvcConfigurationSupportExtensionTests {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
- registry.addViewController("/path");
+ registry.addViewController("/path").setViewName("view");
+ registry.addRedirectViewController("/old", "/new").setStatusCode(HttpStatus.PERMANENT_REDIRECT);
+ registry.addStatusController("/bad", HttpStatus.NOT_FOUND);
}
@Override
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers-minimal.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers-minimal.xml
index fdfe28ff68..130921946d 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers-minimal.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers-minimal.xml
@@ -2,9 +2,15 @@
-
+
+
+
+
+
+
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml
index fe2f06d31e..2f9c585115 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml
@@ -2,17 +2,22 @@
+
-
-
+
+
+
+