SPR-6464 Polish following code review.
This commit is contained in:
@@ -211,7 +211,7 @@ public final class ModelFactory {
|
||||
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
|
||||
}
|
||||
|
||||
if (mavContainer.isResolveView()) {
|
||||
if (!mavContainer.isRequestHandled()) {
|
||||
updateBindingResult(request, mavContainer.getModel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface for resolving method parameters into argument values in the context of a given request.
|
||||
* Strategy interface for resolving method parameters into argument values in
|
||||
* the context of a given request.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
@@ -30,21 +31,27 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
public interface HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is supported by this resolver.
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is
|
||||
* supported by this resolver.
|
||||
*
|
||||
* @param parameter the method parameter to check
|
||||
* @return {@code true} if this resolver supports the supplied parameter; {@code false} otherwise
|
||||
* @return {@code true} if this resolver supports the supplied parameter;
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
boolean supportsParameter(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Resolves a method parameter into an argument value from a given request. A {@link ModelAndViewContainer}
|
||||
* provides access to the model for the request. A {@link WebDataBinderFactory} provides a way to create
|
||||
* a {@link WebDataBinder} instance when needed for data binding and type conversion purposes.
|
||||
* Resolves a method parameter into an argument value from a given request.
|
||||
* A {@link ModelAndViewContainer} provides access to the model for the
|
||||
* request. A {@link WebDataBinderFactory} provides a way to create
|
||||
* a {@link WebDataBinder} instance when needed for data binding and
|
||||
* type conversion purposes.
|
||||
*
|
||||
* @param parameter the method parameter to resolve. This parameter must have previously been passed to
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)} and it must have returned {@code true}
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param parameter the method parameter to resolve. This parameter must
|
||||
* have previously been passed to
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)}
|
||||
* and it must have returned {@code true}
|
||||
* @param mavContainer the ModelAndViewContainer for the current request
|
||||
* @param webRequest the current request
|
||||
* @param binderFactory a factory for creating {@link WebDataBinder} instances
|
||||
* @return the resolved argument value, or {@code null}.
|
||||
|
||||
@@ -20,7 +20,8 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface to handle the value returned from the invocation of a handler method .
|
||||
* Strategy interface to handle the value returned from the invocation of a
|
||||
* handler method .
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
@@ -28,24 +29,27 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
public interface HandlerMethodReturnValueHandler {
|
||||
|
||||
/**
|
||||
* Whether the given {@linkplain MethodParameter method return type} is supported by this handler.
|
||||
* Whether the given {@linkplain MethodParameter method return type} is
|
||||
* supported by this handler.
|
||||
*
|
||||
* @param returnType the method return type to check
|
||||
* @return {@code true} if this handler supports the supplied return type; {@code false} otherwise
|
||||
* @return {@code true} if this handler supports the supplied return type;
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
boolean supportsReturnType(MethodParameter returnType);
|
||||
|
||||
/**
|
||||
* Handle the given return value by adding attributes to the model, setting the view (or view name),
|
||||
* or by writing to the response. {@link HandlerMethodReturnValueHandler} implementations should also
|
||||
* consider whether to set {@link ModelAndViewContainer#setResolveView(boolean)}, which is set to
|
||||
* {@code true} by default and therefore needs to be set to {@code false} explicitly if view
|
||||
* resolution is to be bypassed.
|
||||
* Handle the given return value by adding attributes to the model and
|
||||
* setting a view or setting the
|
||||
* {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
|
||||
* to indicate the response has been handled directly.
|
||||
*
|
||||
* @param returnValue the value returned from the handler method
|
||||
* @param returnType the type of the return value. This type must have previously been passed to
|
||||
* {@link #supportsReturnType(org.springframework.core.MethodParameter)} and it must have returned {@code true}
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param returnType the type of the return value. This type must have
|
||||
* previously been passed to
|
||||
* {@link #supportsReturnType(org.springframework.core.MethodParameter)}
|
||||
* and it must have returned {@code true}
|
||||
* @param mavContainer the ModelAndViewContainer for the current request
|
||||
* @param webRequest the current request
|
||||
* @throws Exception if the return value handling results in an error
|
||||
*/
|
||||
|
||||
@@ -28,14 +28,14 @@ import org.springframework.validation.support.BindingAwareModelMap;
|
||||
* {@link HandlerMethodReturnValueHandler}s during the course of invocation of
|
||||
* a controller method.
|
||||
*
|
||||
* <p>The {@link #setResolveView(boolean)} flag can be used to indicate that
|
||||
* view resolution is not required (e.g. {@code @ResponseBody} method).
|
||||
* <p>The {@link #setRequestHandled} flag can be used to indicate the request
|
||||
* has been handled directly and view resolution is not required.
|
||||
*
|
||||
* <p>A default {@link Model} is created at instantiation and used thereafter.
|
||||
* The {@link #setRedirectModel(ModelMap)} method can be used to provide a
|
||||
* separate model to use potentially in case of a redirect.
|
||||
* The {@link #setUseRedirectModel()} can be used to enable use of the
|
||||
* redirect model if the controller decides to redirect.
|
||||
* <p>A default {@link Model} is automatically created at instantiation.
|
||||
* An alternate model instance may be provided via {@link #setRedirectModel}
|
||||
* for use in a redirect scenario. When {@link #setUseRedirectModel} is set
|
||||
* to {@code true} signalling a redirect scenario, the {@link #getModel()}
|
||||
* returns the redirect model instead of the default model.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -44,12 +44,14 @@ public class ModelAndViewContainer {
|
||||
|
||||
private Object view;
|
||||
|
||||
private boolean resolveView = true;
|
||||
private boolean requestHandled = false;
|
||||
|
||||
private final ModelMap model = new BindingAwareModelMap();
|
||||
|
||||
private ModelMap redirectModel;
|
||||
|
||||
private boolean ignoreDefaultModelOnRedirect = false;
|
||||
|
||||
private boolean useRedirectModel = false;
|
||||
|
||||
/**
|
||||
@@ -99,7 +101,7 @@ public class ModelAndViewContainer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether view resolution is required or not.
|
||||
* Signal a scenario where the request is handled directly.
|
||||
* <p>A {@link HandlerMethodReturnValueHandler} may use this flag to
|
||||
* indicate the response has been fully handled and view resolution
|
||||
* is not required (e.g. {@code @ResponseBody}).
|
||||
@@ -109,54 +111,64 @@ public class ModelAndViewContainer {
|
||||
* a complete response depending on the method return value.
|
||||
* <p>The default value is {@code true}.
|
||||
*/
|
||||
public void setResolveView(boolean resolveView) {
|
||||
this.resolveView = resolveView;
|
||||
public void setRequestHandled(boolean requestHandled) {
|
||||
this.requestHandled = requestHandled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether view resolution is required or not.
|
||||
* Whether the request is handled directly.
|
||||
*/
|
||||
public boolean isResolveView() {
|
||||
return this.resolveView;
|
||||
public boolean isRequestHandled() {
|
||||
return this.requestHandled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default model created at instantiation or the one provided
|
||||
* via {@link #setRedirectModel(ModelMap)} as long as it has been enabled
|
||||
* via {@link #setUseRedirectModel()}.
|
||||
* Return the model to use. This is either the default model created at
|
||||
* instantiation or the redirect model if {@link #setUseRedirectModel}
|
||||
* is set to {@code true}. If a redirect model was never provided via
|
||||
* {@link #setRedirectModel}, return the default model unless
|
||||
* {@link #setIgnoreDefaultModelOnRedirect} is set to {@code true}.
|
||||
*/
|
||||
public ModelMap getModel() {
|
||||
if ((this.redirectModel != null) && this.useRedirectModel) {
|
||||
if (!this.useRedirectModel) {
|
||||
return this.model;
|
||||
}
|
||||
else if (this.redirectModel != null) {
|
||||
return this.redirectModel;
|
||||
}
|
||||
else {
|
||||
return this.model;
|
||||
return this.ignoreDefaultModelOnRedirect ? new ModelMap() : this.model;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a model instance to use in case the controller redirects.
|
||||
* Note that {@link #setUseRedirectModel()} must also be called in order
|
||||
* to enable use of the redirect model.
|
||||
* Provide a separate model instance to use in a redirect scenario.
|
||||
* The provided additional model however is not used used unless
|
||||
* {@link #setUseRedirectModel(boolean)} gets set to {@code true} to signal
|
||||
* a redirect scenario.
|
||||
*/
|
||||
public void setRedirectModel(ModelMap redirectModel) {
|
||||
this.redirectModel = redirectModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the redirect model provided via
|
||||
* {@link #setRedirectModel(ModelMap)} or {@code null} if not provided.
|
||||
* When set to {@code true} the default model is never used in a redirect
|
||||
* scenario. So if a redirect model is not available, an empty model is
|
||||
* used instead.
|
||||
* <p>When set to {@code false} the default model can be used in a redirect
|
||||
* scenario if a redirect model is not available.
|
||||
* <p>The default setting is {@code false}.
|
||||
*/
|
||||
public ModelMap getRedirectModel() {
|
||||
return this.redirectModel;
|
||||
public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
|
||||
this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the redirect model provided via
|
||||
* {@link #setRedirectModel(ModelMap)} should be used.
|
||||
* Signal the conditions for using a redirect model are in place -- e.g.
|
||||
* the controller has requested a redirect.
|
||||
*/
|
||||
public void setUseRedirectModel() {
|
||||
this.useRedirectModel = true;
|
||||
public void setUseRedirectModel(boolean useRedirectModel) {
|
||||
this.useRedirectModel = useRedirectModel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +222,7 @@ public class ModelAndViewContainer {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("ModelAndViewContainer: ");
|
||||
if (isResolveView()) {
|
||||
if (!isRequestHandled()) {
|
||||
if (isViewReference()) {
|
||||
sb.append("reference to view with name '").append(this.view).append("'");
|
||||
}
|
||||
@@ -220,7 +232,7 @@ public class ModelAndViewContainer {
|
||||
sb.append("; model is ").append(getModel());
|
||||
}
|
||||
else {
|
||||
sb.append("View resolution not required");
|
||||
sb.append("Request handled directly");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -724,29 +724,4 @@ public abstract class WebUtils {
|
||||
return urlPath.substring(begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the path from the given URL by removing the query at the end
|
||||
* and the scheme and authority in the front, if present.
|
||||
* @param url a URL, never {@code null}
|
||||
* @return the extracted URL path
|
||||
*/
|
||||
public static String extractUrlPath(String url) {
|
||||
// Remove query/fragment
|
||||
int end = url.indexOf('?');
|
||||
if (end == -1) {
|
||||
end = url.indexOf('#');
|
||||
if (end == -1) {
|
||||
end = url.length();
|
||||
}
|
||||
}
|
||||
url = url.substring(0, end);
|
||||
// Remove scheme + authority
|
||||
int start = url.indexOf("://");
|
||||
if (start != -1) {
|
||||
start = url.indexOf('/', start + 3);
|
||||
url = (start != -1 ) ? url.substring(start) : "";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.method.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ui.ModelMap;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ModelAndViewContainer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelAndViewContainerTests {
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mavContainer = new ModelAndViewContainer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getModel() {
|
||||
this.mavContainer.addAttribute("name", "value");
|
||||
assertEquals(1, this.mavContainer.getModel().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getModelRedirectModel() {
|
||||
ModelMap redirectModel = new ModelMap("name", "redirectValue");
|
||||
this.mavContainer.setRedirectModel(redirectModel);
|
||||
this.mavContainer.addAttribute("name", "value");
|
||||
|
||||
assertEquals("Default model should be used if not in redirect scenario",
|
||||
"value", this.mavContainer.getModel().get("name"));
|
||||
|
||||
this.mavContainer.setUseRedirectModel(true);
|
||||
|
||||
assertEquals("Redirect model should be used in redirect scenario",
|
||||
"redirectValue", this.mavContainer.getModel().get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getModelIgnoreDefaultModelOnRedirect() {
|
||||
this.mavContainer.addAttribute("name", "value");
|
||||
this.mavContainer.setUseRedirectModel(true);
|
||||
|
||||
assertEquals("Default model should be used since no redirect model was provided",
|
||||
1, this.mavContainer.getModel().size());
|
||||
|
||||
this.mavContainer.setIgnoreDefaultModelOnRedirect(true);
|
||||
|
||||
assertEquals("Empty model should be returned if no redirect model is available",
|
||||
0, this.mavContainer.getModel().size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,15 +64,4 @@ public class WebUtilsTests {
|
||||
assertEquals("view.html", WebUtils.extractFullFilenameFromUrlPath("/products/view.html?param=/path/a.do"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractUriPath() {
|
||||
assertEquals("", WebUtils.extractUrlPath("http://example.com"));
|
||||
assertEquals("/", WebUtils.extractUrlPath("http://example.com/"));
|
||||
assertEquals("/rfc/rfc3986.txt", WebUtils.extractUrlPath("http://www.ietf.org/rfc/rfc3986.txt"));
|
||||
assertEquals("/over/there", WebUtils.extractUrlPath("http://example.com/over/there?name=ferret#nose"));
|
||||
assertEquals("/over/there", WebUtils.extractUrlPath("http://example.com/over/there#nose"));
|
||||
assertEquals("/over/there", WebUtils.extractUrlPath("/over/there?name=ferret#nose"));
|
||||
assertEquals("/over/there", WebUtils.extractUrlPath("/over/there?url=http://example.com"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user