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
This commit is contained in:
parent
0bbb7704b5
commit
1ad22b922f
|
@ -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());
|
||||
|
|
|
@ -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 {
|
|||
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<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 {
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <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 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 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<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 {
|
|||
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<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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 selects the view.
|
||||
In effect the DefaultRequestToViewNameTranslator translates "/foo/bar" to "foo/bar".
|
||||
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 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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<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 {
|
|||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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…
Reference in New Issue