Browse Source

Enhance view controller MVC config

This change adds support for configuring redirect view controllers and
also status controllers to the MVC Java config and the MVC namespace.

Issue: SPR-11543
pull/552/merge
Rossen Stoyanchev 11 years ago
parent
commit
1ad22b922f
  1. 2
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java
  2. 73
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java
  3. 93
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/RedirectViewControllerRegistration.java
  4. 37
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistration.java
  5. 44
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java
  6. 7
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
  7. 104
      spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd
  8. 56
      spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java
  9. 78
      spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistryTests.java
  10. 21
      spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java
  11. 10
      spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers-minimal.xml
  12. 11
      spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml

2
spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java

@ -35,6 +35,8 @@ public class MvcNamespaceHandler extends NamespaceHandlerSupport { @@ -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());

73
spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java

@ -19,19 +19,32 @@ package org.springframework.web.servlet.config; @@ -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:
* <ul>
* <li>{@code <view-controller>}
* <li>{@code <redirect-view-controller>}
* <li>{@code <status-controller>}
* </ul>
*
* <p>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 { @@ -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 { @@ -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<String, BeanDefinition> urlMap;
if (handlerMapping.getPropertyValues().contains("urlMap")) {
urlMap = (Map<String, BeanDefinition>) handlerMapping.getPropertyValues().getPropertyValue("urlMap").getValue();
if (hm.getPropertyValues().contains("urlMap")) {
urlMap = (Map<String, BeanDefinition>) hm.getPropertyValues().getPropertyValue("urlMap").getValue();
}
else {
urlMap = new ManagedMap<String, BeanDefinition>();
handlerMapping.getPropertyValues().add("urlMap", urlMap);
hm.getPropertyValues().add("urlMap", urlMap);
}
urlMap.put(element.getAttribute("path"), controller);
@ -91,4 +129,21 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser { @@ -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;
}
}

93
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/RedirectViewControllerRegistration.java

@ -0,0 +1,93 @@ @@ -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.
*
* <p>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.
*
* <p>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.
*
* <p>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;
}
}

37
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistration.java

@ -16,6 +16,7 @@ @@ -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 { @@ -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 { @@ -44,17 +42,28 @@ public class ViewControllerRegistration {
/**
* Set the view name to return.
* Set the status code to set on the response. Optional.
*
* <p>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.
*
* <p>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".
* <p>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 { @@ -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;
}
}

44
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java

@ -21,14 +21,13 @@ import java.util.LinkedHashMap; @@ -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; @@ -36,13 +35,17 @@ import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
*/
public class ViewControllerRegistry {
private final List<ViewControllerRegistration> registrations = new ArrayList<ViewControllerRegistration>();
private final List<ViewControllerRegistration> registrations = new ArrayList<ViewControllerRegistration>(4);
private final List<RedirectViewControllerRegistration> redirectRegistrations =
new ArrayList<RedirectViewControllerRegistration>(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 { @@ -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 { @@ -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<String, Object> urlMap = new LinkedHashMap<String, Object>();
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);

7
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java

@ -133,8 +133,11 @@ public interface WebMvcConfigurer { @@ -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);

104
spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd

@ -574,27 +574,115 @@ @@ -574,27 +574,115 @@
<xsd:element name="view-controller">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.web.servlet.mvc.ParameterizableViewController"><![CDATA[
Defines a simple Controller that selects a view to render the response.
<xsd:documentation source="java:org.springframework.web.servlet.mvc.ParameterizableViewController"><![CDATA[
Map a simple (logic-less) view controller to a specific URL path (or pattern)
in order to render a response with a pre-configured status code and view.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="path" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The URL path or (path pattern) the controller is mapped to.
The URL path (or pattern) the controller is mapped to.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="view-name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the view to render. Optional.
Set the view name to return. Optional.
If not specified, the view controller will return null as the
view name in which case the configured RequestToViewNameTranslator
will select the view name. The DefaultRequestToViewNameTranslator
for example translates "/foo/bar" to "foo/bar".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="status-code" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
Set the status code to set on the response. Optional.
If not set the response status will be 200 (OK).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="redirect-view-controller">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.servlet.mvc.ParameterizableViewController"><![CDATA[
Map a simple (logic-less) view controller to the given URL path (or pattern)
in order to redirect to another URL.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="path" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The URL path (or pattern) the controller is mapped to.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="redirect-url" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
By default the redirect URL is expected to be relative to the current ServletContext,
i.e. as relative to the web application root.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="status-code" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
Set the specific redirect 3xx status code to use.
If not specified, the view controller will return null as the view name in which case
the configured RequestToViewNameTranslator selects the view.
In effect the DefaultRequestToViewNameTranslator translates "/foo/bar" to "foo/bar".
If not set, org.springframework.web.servlet.view.RedirectView
will select MOVED_TEMPORARILY (302) by default.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="context-relative" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation><![CDATA[
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.
By default set to "true".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="keep-query-params" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether to propagate the query parameters of the current request through to the target redirect URL.
By default set to "false".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="status-controller">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.web.servlet.mvc.ParameterizableViewController"><![CDATA[
Map a simple (logic-less) controller to the given URL path (or pattern) in order to
sets the response status to the given code without rendering a body.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="path" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The URL path (or pattern) the controller is mapped to.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="status-code" type="xsd:int" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The status code to set on the response.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>

56
spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java

@ -37,6 +37,7 @@ import org.springframework.core.io.ClassPathResource; @@ -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; @@ -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 @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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();
}

78
spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistryTests.java

@ -17,15 +17,22 @@ @@ -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 { @@ -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 { @@ -50,21 +63,59 @@ public class ViewControllerRegistryTests {
@Test
public void addViewController() {
this.registry.addViewController("/path").setViewName("viewName");
Map<String, ?> 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<String, ?> 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 { @@ -76,9 +127,24 @@ public class ViewControllerRegistryTests {
assertEquals(2, handlerMapping.getOrder());
}
private ParameterizableViewController getController(String path) {
Map<String, ?> 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();
}
}

21
spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java

@ -26,6 +26,7 @@ import org.junit.Before; @@ -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 { @@ -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 { @@ -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 { @@ -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

10
spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers-minimal.xml

@ -2,9 +2,15 @@ @@ -2,9 +2,15 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
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.xsd">
<mvc:view-controller path="/" view-name="home"/>
<mvc:view-controller path="/path" view-name="home" />
<mvc:redirect-view-controller path="/old" redirect-url="/new" />
<mvc:status-controller path="/bad" status-code="404" />
</beans>

11
spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-controllers.xml

@ -2,17 +2,22 @@ @@ -2,17 +2,22 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
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.xsd">
<mvc:annotation-driven/>
<mvc:view-controller path="/foo"/>
<mvc:view-controller path="/bar" view-name="baz"/>
<mvc:view-controller path="/" view-name="root"/>
<mvc:redirect-view-controller path="/old" redirect-url="/new"
context-relative="false" status-code="308" keep-query-params="true" />
<mvc:status-controller path="/bad" status-code="404" />
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>

Loading…
Cancel
Save