SPR-6464 Add RedirectModel
This commit is contained in:
@@ -60,7 +60,6 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequ
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.support.ResponseContext;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHandlerMethodExceptionResolver} that supports using {@link ExceptionHandler}-annotated methods
|
||||
@@ -214,10 +213,10 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod,
|
||||
Exception ex) {
|
||||
Exception exception) {
|
||||
if (handlerMethod != null) {
|
||||
ExceptionMethodMapping mapping = getExceptionMethodMapping(handlerMethod);
|
||||
Method method = mapping.getMethod(ex);
|
||||
Method method = mapping.getMethod(exception);
|
||||
|
||||
if (method != null) {
|
||||
Object handler = handlerMethod.getBean();
|
||||
@@ -232,9 +231,8 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||
}
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
ResponseContext responseContext = new ResponseContext(webRequest, mavContainer);
|
||||
|
||||
exceptionHandler.invokeAndHandle(webRequest, mavContainer, ex, responseContext);
|
||||
exceptionHandler.invokeAndHandle(webRequest, mavContainer, exception);
|
||||
|
||||
if (!mavContainer.isResolveView()) {
|
||||
return new ModelAndView();
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
@@ -84,6 +85,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMeth
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.PathVariableMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.RedirectModelMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestPartMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletCookieValueMethodArgumentResolver;
|
||||
@@ -91,7 +93,8 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletMode
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.support.ResponseContext;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectModel;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
@@ -366,6 +369,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
argumentResolvers.addResolver(new ServletRequestMethodArgumentResolver());
|
||||
argumentResolvers.addResolver(new ServletResponseMethodArgumentResolver());
|
||||
argumentResolvers.addResolver(new HttpEntityMethodProcessor(messageConverters));
|
||||
argumentResolvers.addResolver(new RedirectModelMethodArgumentResolver());
|
||||
argumentResolvers.addResolver(new ModelMethodProcessor());
|
||||
argumentResolvers.addResolver(new ErrorsMethodArgumentResolver());
|
||||
|
||||
@@ -512,27 +516,32 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod);
|
||||
ModelFactory modelFactory = getModelFactory(handlerMethod);
|
||||
|
||||
FlashMap flashMap = (FlashMap) request.getAttribute(FlashMapManager.PREVIOUS_FLASH_MAP_ATTRIBUTE);
|
||||
FlashMap previousFlashMap = (FlashMap) request.getAttribute(FlashMapManager.PREVIOUS_FLASH_MAP_ATTRIBUTE);
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
mavContainer.addAllAttributes(flashMap);
|
||||
mavContainer.addAllAttributes(previousFlashMap);
|
||||
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
|
||||
|
||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
ResponseContext responseContext = new ResponseContext(webRequest, mavContainer);
|
||||
|
||||
requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus, responseContext);
|
||||
requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus);
|
||||
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
|
||||
|
||||
if (!mavContainer.isResolveView()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
|
||||
ModelMap model = mavContainer.getModel();
|
||||
ModelAndView mav = new ModelAndView().addAllObjects(model);
|
||||
mav.setViewName(mavContainer.getViewName());
|
||||
if (!mavContainer.isViewReference()) {
|
||||
mav.setView((View) mavContainer.getView());
|
||||
}
|
||||
if (model instanceof RedirectModel) {
|
||||
RedirectModel redirectModel = (RedirectModel) model;
|
||||
FlashMap currentFlashMap = RequestContextUtils.getFlashMap(request);
|
||||
currentFlashMap.putAll(redirectModel.getFlashAttributes());
|
||||
}
|
||||
return mav;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mvc.method.annotation.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectModel;
|
||||
|
||||
/**
|
||||
* Resolves {@link RedirectModel} method arguments.
|
||||
*
|
||||
* <p>This resolver must be listed ahead of the {@link ModelMethodProcessor},
|
||||
* which also resolves arguments of type {@link Model}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RedirectModelMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return RedirectModel.class.equals(parameter.getParameterType());
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
|
||||
ModelMap implicitModel = mavContainer.getModel();
|
||||
RedirectModel redirectModel = new RedirectModel(dataBinder, implicitModel);
|
||||
mavContainer.setRedirectModel(redirectModel);
|
||||
return redirectModel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@@ -26,18 +24,20 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.RequestToViewNameTranslator;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator;
|
||||
|
||||
/**
|
||||
* Handles return values that are of type {@code void}, {@code String} (i.e. logical view name), or {@link View}.
|
||||
* Handles return values that are of type {@code void}, {@code String} (i.e.
|
||||
* logical view name), or {@link View}.
|
||||
*
|
||||
* <p>A {@code null} return value, either due to a void return type or as the actual value returned from a
|
||||
* method is left unhandled, leaving it to the configured {@link RequestToViewNameTranslator} to resolve the
|
||||
* request to an actual view name. By default it is the {@link DefaultRequestToViewNameTranslator}.
|
||||
* <p>A {@code null} return value, either due to a void return type or as the
|
||||
* actual value returned from a method is left unhandled, leaving it to the
|
||||
* configured {@link RequestToViewNameTranslator} to resolve the request to
|
||||
* an actual view name.
|
||||
*
|
||||
* <p>Since a {@link String} return value may handled in different ways, especially in combination with method
|
||||
* annotations such as @{@link ModelAttribute} and @{@link ResponseBody}, this handler should be ordered after
|
||||
* return value handlers that support method annotations.
|
||||
* <p>Since a {@link String} return value may be handled in combination with
|
||||
* method annotations such as @{@link ModelAttribute} or @{@link ResponseBody},
|
||||
* this handler should be ordered after return value handlers that support
|
||||
* method annotations.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -57,17 +57,44 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
|
||||
return;
|
||||
}
|
||||
if (returnValue instanceof String) {
|
||||
mavContainer.setViewName((String) returnValue);
|
||||
String viewName = (String) returnValue;
|
||||
mavContainer.setViewName(viewName);
|
||||
if (isRedirectViewName(viewName)) {
|
||||
mavContainer.useRedirectModel();
|
||||
}
|
||||
}
|
||||
else if (returnValue instanceof View){
|
||||
mavContainer.setView(returnValue);
|
||||
View view = (View) returnValue;
|
||||
mavContainer.setView(view);
|
||||
if (isRedirectView(view)) {
|
||||
mavContainer.useRedirectModel();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// should not happen
|
||||
Method method = returnType.getMethod();
|
||||
String returnTypeName = returnType.getParameterType().getName();
|
||||
throw new UnsupportedOperationException("Unknown return type: " + returnTypeName + " in method: " + method);
|
||||
throw new UnsupportedOperationException("Unknown return type: " +
|
||||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given view name is a redirect view reference.
|
||||
* @param viewName the view name to check, never {@code null}
|
||||
* @return "true" if the given view name is recognized as a redirect view
|
||||
* reference; "false" otherwise.
|
||||
*/
|
||||
protected boolean isRedirectViewName(String viewName) {
|
||||
return viewName.startsWith("redirect:");
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given View instance is a redirect view.
|
||||
* @param view a view instance, never {@code null}
|
||||
* @return "true" if the given view is recognized as a redirect View;
|
||||
* "false" otherwise.
|
||||
*/
|
||||
protected boolean isRedirectView(View view) {
|
||||
return "RedirectView".equals(view.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mvc.method.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* Provides annotated controller methods with convenience methods for setting
|
||||
* up response with a view name that will result in a redirect.
|
||||
*
|
||||
* <p>An instance of this class is obtained via {@link ResponseContext#redirect}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RedirectResponse {
|
||||
|
||||
private final NativeWebRequest webRequest;
|
||||
|
||||
private final ModelAndViewContainer mavContainer;
|
||||
|
||||
RedirectResponse(NativeWebRequest webRequest, ModelAndViewContainer mavContainer) {
|
||||
this.webRequest = webRequest;
|
||||
this.mavContainer = mavContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a URI template variable to use to expand the URI template into a URL.
|
||||
* <p><strong>Note:</strong> URI template variables from the current
|
||||
* request are automatically used when expanding the redirect URI template.
|
||||
* They don't need to be added explicitly here.
|
||||
*/
|
||||
public RedirectResponse uriVariable(String name, Object value) {
|
||||
this.mavContainer.addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a URI template variable to use to expand the URI template into a URL.
|
||||
* The name of the variable is selected using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
* <p><strong>Note:</strong> URI template variables from the current
|
||||
* request are automatically used when expanding the redirect URI template.
|
||||
* They don't need to be added explicitly here.
|
||||
*/
|
||||
public RedirectResponse uriVariable(Object value) {
|
||||
this.mavContainer.addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query parameter to append to the redirect URL.
|
||||
*/
|
||||
public RedirectResponse queryParam(String name, Object value) {
|
||||
this.mavContainer.addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query parameter to append to the redirect URL.
|
||||
* The name of the parameter is selected using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
*/
|
||||
public RedirectResponse queryParam(Object value) {
|
||||
this.mavContainer.addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flash attribute to save and make available in the model of the
|
||||
* target controller method after the redirect.
|
||||
*/
|
||||
public RedirectResponse flashAttribute(String name, Object value) {
|
||||
getFlashMap().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flash attribute to save and make available in the model of the
|
||||
* target controller method after the redirect.
|
||||
* The name of the attribute is selected using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
*/
|
||||
public RedirectResponse flashAttribute(Object attributeValue) {
|
||||
Assert.notNull(attributeValue, "Model object must not be null");
|
||||
if (attributeValue instanceof Collection && ((Collection) attributeValue).isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
return flashAttribute(Conventions.getVariableName(attributeValue), attributeValue);
|
||||
}
|
||||
|
||||
private FlashMap getFlashMap() {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return RequestContextUtils.getFlashMap(servletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mvc.method.support;
|
||||
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Provides annotated controllers with convenience methods for setting up view
|
||||
* resolution.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ResponseContext {
|
||||
|
||||
private final NativeWebRequest webRequest;
|
||||
|
||||
private final ModelAndViewContainer mavContainer;
|
||||
|
||||
public ResponseContext(NativeWebRequest webRequest, ModelAndViewContainer mavContainer) {
|
||||
this.webRequest = webRequest;
|
||||
this.mavContainer = mavContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up view resolution based on the given view name and the implicit model
|
||||
* of the current request.
|
||||
*/
|
||||
public ViewResponse view(String viewName) {
|
||||
this.mavContainer.setViewName(viewName);
|
||||
return new ViewResponse(this.mavContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up view resolution for a redirect. This method clears the implicit
|
||||
* model. Use convenience methods on the returned {@link RedirectResponse}
|
||||
* instance to add URI variables, query parameters, and flash attributes
|
||||
* as necessary.
|
||||
* @param redirectUri a URI template either relative to the current URL or
|
||||
* absolute; do not prefix with "redirect:"
|
||||
*/
|
||||
public RedirectResponse redirect(String redirectUri) {
|
||||
if (!redirectUri.startsWith("redirect:")) {
|
||||
redirectUri = "redirect:" + redirectUri;
|
||||
}
|
||||
this.mavContainer.getModel().clear();
|
||||
this.mavContainer.setViewName(redirectUri);
|
||||
return new RedirectResponse(this.webRequest, this.mavContainer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mvc.method.support;
|
||||
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Provides annotated controller methods with convenience methods for setting
|
||||
* up a response with a view name that does not have the "redirect:" prefix .
|
||||
*
|
||||
* <p>An instance of this class is obtained via {@link ResponseContext#view}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ViewResponse {
|
||||
|
||||
private final ModelAndViewContainer mavContainer;
|
||||
|
||||
ViewResponse(ModelAndViewContainer mavContainer) {
|
||||
this.mavContainer = mavContainer;
|
||||
}
|
||||
|
||||
public ViewResponse attribute(String attributeName, Object attributeValue) {
|
||||
this.mavContainer.addAttribute(attributeName, attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewResponse attribute(Object attributeValue) {
|
||||
this.mavContainer.addAttribute(attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mvc.support;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.DataBinder;
|
||||
|
||||
/**
|
||||
* A {@link Model} implementation that controllers can use when they wish to
|
||||
* redirect. For a redirect a controller needs to use an empty model and
|
||||
* only add those attributes that will be used in the redirect URL --
|
||||
* either embedded as URI template variables or appended as query parameters.
|
||||
* To be used in the URL such attributes need to be formatted as String
|
||||
* values. Alternatively a controller may choose to keep attributes in
|
||||
* flash storage instead for the duration of the redirect.
|
||||
*
|
||||
* <p>A RedirectModel serves the above needs as follows:
|
||||
* <ul>
|
||||
* <li>Formats attribute values as Strings before adding them using a
|
||||
* registered {@link Converter}, {@link Formatter}, {@link PropertyEditor},
|
||||
* or the attribute's {@link #toString()} method.
|
||||
* <li>Wraps the the model of the current request and provides a method to
|
||||
* copy attributes from it (see {@link #addModelAttributes(String...)}).
|
||||
* <li>Provides methods to store attributes candidate for flash storage.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that a RedirectModel will not be used unless the controller decides
|
||||
* to redirect.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RedirectModel extends ExtendedModelMap {
|
||||
|
||||
private final DataBinder dataBinder;
|
||||
|
||||
private final ModelMap flashAttributes = new ModelMap();
|
||||
|
||||
private final ModelMap implicitModel;
|
||||
|
||||
/**
|
||||
* Create a new instance without a DataBinder. Attribute values will be
|
||||
* formatted via {@link #toString()}.
|
||||
*/
|
||||
public RedirectModel() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance providing a DataBinder to use for formatting
|
||||
* attribute values.
|
||||
* @param dataBinder a DataBinder for converting attribute values to String.
|
||||
* @param implicitModel the implicit model for the current request;
|
||||
* used in conjunction with {@link #addModelAttributes(String...)}
|
||||
* to copy attributes from the implicit model to the redirect model.
|
||||
*/
|
||||
public RedirectModel(DataBinder dataBinder, ModelMap implicitModel) {
|
||||
this.dataBinder = dataBinder;
|
||||
this.implicitModel = implicitModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the attributes candidate for flash storage.
|
||||
*/
|
||||
public Map<String, ?> getFlashAttributes() {
|
||||
return flashAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attribute. Before being added the attribute value is formatted as
|
||||
* a String in preparation for use in the redirect URL. If the attribute
|
||||
* value is null it is not be added to the model.
|
||||
* @param attributeName the attribute name; never null
|
||||
* @param attributeValue the attribute value; skipped if null
|
||||
*/
|
||||
public RedirectModel addAttribute(String attributeName, Object attributeValue) {
|
||||
if (attributeValue != null) {
|
||||
super.addAttribute(attributeName, formatValue(attributeValue));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private String formatValue(Object value) {
|
||||
return (dataBinder != null) ? dataBinder.convertIfNecessary(value, String.class) : value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attribute using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
* Before being added the attribute value is formatted as a String.
|
||||
* @param attributeValue the attribute value; never null
|
||||
*/
|
||||
public RedirectModel addAttribute(Object attributeValue) {
|
||||
super.addAttribute(attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all attributes in the supplied <code>Collection</code> into this
|
||||
* Model using attribute name generation for each element.
|
||||
* @see #addAttribute(Object)
|
||||
*/
|
||||
public RedirectModel addAllAttributes(Collection<?> attributeValues) {
|
||||
super.addAllAttributes(attributeValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all supplied attributes into this redirect model.
|
||||
* @see #addAttribute(String, Object)
|
||||
*/
|
||||
public RedirectModel addAllAttributes(Map<String, ?> attributes) {
|
||||
if (attributes != null) {
|
||||
for (String key : attributes.keySet()) {
|
||||
addAttribute(key, attributes.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all supplied attributes into this redirect model with with existing
|
||||
* attributes of the same name taking precedence (i.e. not getting replaced).
|
||||
* @see #addAttribute(String, Object)
|
||||
*/
|
||||
public RedirectModel mergeAttributes(Map<String, ?> attributes) {
|
||||
if (attributes != null) {
|
||||
for (String key : attributes.keySet()) {
|
||||
if (!containsKey(key)) {
|
||||
addAttribute(key, attributes.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the attributes specified by name from the "implicit" model of the
|
||||
* current request to this redirect model instance.
|
||||
* @param attributeNames the names of attributes present in the implicit model.
|
||||
* attribute names are required to be present; if an attribute is present
|
||||
* but is null, it is skipped
|
||||
* @see #addAttribute(String, Object)
|
||||
*/
|
||||
public RedirectModel addModelAttributes(String... attributeNames) {
|
||||
Assert.notNull(this.implicitModel, "The implicit model has not been set.");
|
||||
for (String name : attributeNames) {
|
||||
Assert.isTrue(this.implicitModel.containsAttribute(name), name + " not found in implicit model");
|
||||
Object value = this.implicitModel.get(name);
|
||||
addAttribute(name, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given attribute as a candidate for flash storage.
|
||||
* @param attributeName the flash attribute name; never null
|
||||
* @param attributeValue the flash attribute value; may be null
|
||||
*/
|
||||
public RedirectModel addFlashAttribute(String attributeName, Object attributeValue) {
|
||||
this.flashAttributes.addAttribute(attributeName, attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given attribute value as a candidate for flash storage using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
* @param attributeValue the flash attribute value; never null
|
||||
*/
|
||||
public RedirectModel addFlashAttribute(Object attributeValue) {
|
||||
this.flashAttributes.addAttribute(attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user