ModelAndViewContainer related refinements

This commit is contained in:
Rossen Stoyanchev
2011-04-07 18:09:28 +00:00
parent 3d8b476f58
commit 6fe0ff9e7b
47 changed files with 547 additions and 1073 deletions

View File

@@ -41,7 +41,6 @@ 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.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -67,13 +66,14 @@ import org.springframework.web.method.annotation.support.RequestHeaderMapMethodA
import org.springframework.web.method.annotation.support.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.support.RequestParamMapMethodArgumentResolver;
import org.springframework.web.method.annotation.support.RequestParamMethodArgumentResolver;
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
@@ -459,13 +459,25 @@ public class RequestMappingHandlerMethodAdapter extends AbstractHandlerMethodAda
ServletWebRequest webRequest = new ServletWebRequest(request, response);
SessionStatus sessionStatus = new SimpleSessionStatus();
ModelMap implicitModel = modelFactory.createModel(webRequest, requestMethod);
ModelAndView mav = requestMethod.invokeAndHandle(webRequest, implicitModel, sessionStatus);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, requestMethod);
requestMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus);
ModelMap actualModel = (mav != null) ? mav.getModelMap() : null;
modelFactory.updateAttributes(webRequest, sessionStatus, actualModel, implicitModel);
return mav;
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
if (!mavContainer.isResolveView()) {
return null;
}
else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (mavContainer.getView() != null) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
}
private WebDataBinderFactory createDataBinderFactory(HandlerMethod handlerMethod) {

View File

@@ -32,8 +32,6 @@ 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.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.support.WebArgumentResolver;
@@ -47,9 +45,10 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
@@ -60,17 +59,15 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebA
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
/**
* An extension of {@link AbstractHandlerMethodExceptionResolver} that matches thrown exceptions to
* {@link ExceptionHandler @ExceptionHandler} methods in the handler. If a match is found the
* exception-handling method is invoked to process the request.
* A {@link AbstractHandlerMethodExceptionResolver} that matches thrown exceptions to {@link ExceptionHandler}-annotated
* methods. If a match is found the exception-handling method is invoked to process the request.
*
* <p>See {@link ExceptionHandler} for information on supported method arguments and return values
* for exception-handling methods. You can customize method argument resolution and return value
* processing through the various bean properties in this class.
* <p>See {@link ExceptionHandler} for information on supported method arguments and return values for exception-handling
* methods. You can customize method argument resolution and return value processing through the various bean properties
* in this class.
*
* @author Rossen Stoyanchev
* @since 3.1
* @see #setMessageConverters(HttpMessageConverter[])
*/
public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandlerMethodExceptionResolver implements
InitializingBean {
@@ -79,8 +76,6 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
private HttpMessageConverter<?>[] messageConverters;
private ModelAndViewResolver[] customModelAndViewResolvers;
private final Map<Class<?>, ExceptionMethodMapping> exceptionMethodMappingCache =
new ConcurrentHashMap<Class<?>, ExceptionMethodMapping>();
@@ -102,9 +97,11 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
}
/**
* Set a custom ArgumentResolvers to use for special method parameter types.
* Set a custom ArgumentResolver to use for special method parameter types.
* <p>Such a custom ArgumentResolver will kick in first, having a chance to resolve
* an argument value before the standard argument handling kicks in.
* <p>Note: this is provided for backward compatibility. The preferred way to do this is to
* implement a {@link HandlerMethodArgumentResolver}.
*/
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
@@ -114,6 +111,8 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
* Set one or more custom ArgumentResolvers to use for special method parameter types.
* <p>Any such custom ArgumentResolver will kick in first, having a chance to resolve
* an argument value before the standard argument handling kicks in.
* <p>Note: this is provided for backward compatibility. The preferred way to do this is to
* implement a {@link HandlerMethodArgumentResolver}.
*/
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
this.customArgumentResolvers = argumentResolvers;
@@ -127,24 +126,6 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
this.messageConverters = messageConverters;
}
/**
* Set a custom ModelAndViewResolvers to use for special method return types.
* <p>Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
* a return value before the standard ModelAndView handling kicks in.
*/
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
}
/**
* Set one or more custom ModelAndViewResolvers to use for special method return types.
* <p>Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
* a return value before the standard ModelAndView handling kicks in.
*/
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
this.customModelAndViewResolvers = customModelAndViewResolvers;
}
/**
* Set the {@link HandlerMethodArgumentResolver}s to use to resolve argument values for
* {@link ExceptionHandler} methods. This is an optional property.
@@ -207,9 +188,14 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
returnValueHandlers.registerReturnValueHandler(new HttpEntityMethodProcessor(messageConverters));
// Default handler
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(customModelAndViewResolvers));
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(null));
}
/**
* Attempts to find an {@link ExceptionHandler}-annotated method that can handle the thrown exception.
* The exception-handling method, if found, is invoked resulting in a {@link ModelAndView}.
* @return a {@link ModelAndView} if a matching exception-handling method was found, or {@code null} otherwise
*/
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response,
@@ -226,13 +212,25 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
exceptionHandler.setHandlerMethodReturnValueHandlers(returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelMap model = new ExtendedModelMap();
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking exception-handling method: " + exceptionHandler);
}
ModelAndView mav = exceptionHandler.invokeAndHandle(webRequest , model , ex);
return (mav != null) ? mav : new ModelAndView();
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
exceptionHandler.invokeAndHandle(webRequest, mavContainer, ex);
if (!mavContainer.isResolveView()) {
return new ModelAndView();
}
else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (mavContainer.getView() != null) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
}
catch (Exception invocationEx) {
logger.error("Invoking exception-handling method resulted in exception : " +
@@ -244,6 +242,9 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
return null;
}
/**
* @return an {@link ExceptionMethodMapping} for the the given handler method, never {@code null}
*/
private ExceptionMethodMapping getExceptionMethodMapping(HandlerMethod handlerMethod) {
Class<?> handlerType = handlerMethod.getBeanType();
ExceptionMethodMapping mapping = exceptionMethodMappingCache.get(handlerType);
@@ -256,7 +257,7 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
}
/**
* Pre-built MethodFilter that matches {@link ExceptionHandler @ExceptionHandler} methods.
* MethodFilter that matches {@link ExceptionHandler @ExceptionHandler} methods.
*/
public static MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {

View File

@@ -43,7 +43,7 @@ public class ServletInitBinderMethodDataBinderFactory extends InitBinderMethodDa
}
/**
* {@inheritDoc} creates a Servlet data binder.
* Creates a Servlet data binder.
*/
@Override
protected WebDataBinder createBinderInstance(Object target, String objectName) {

View File

@@ -20,23 +20,31 @@ import java.io.IOException;
import java.lang.reflect.Method;
import org.springframework.http.HttpStatus;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
/**
* Extends {@link InvocableHandlerMethod} with the ability to handle the return value of the invocation
* resulting in a {@link ModelAndView} according to the {@link HandlerAdapter} contract.
* Extends {@link InvocableHandlerMethod} with the ability to handle the return value through registered
* {@link HandlerMethodArgumentResolver}s. If the handler method is annotated with {@link ResponseStatus},
* the status on the response is set accordingly after method invocation but before return value handling.
*
* <p>Return value handling may be skipped entirely if the handler method returns a {@code null} (or is a
* {@code void} method) and one of the following other conditions is true:
* <ul>
* <li>One of the {@link HandlerMethodArgumentResolver}s set the {@link ModelAndViewContainer#setResolveView(boolean)}
* flag to {@code false}. This is the case when a method argument allows the handler method access to the response.
* <li>The request qualifies as being not modified according to {@link ServletWebRequest#isNotModified()}.
* This is used in conjunction with a "Last-Modified" header or ETag.
* <li>The status on the response was set as a result of a {@link ResponseStatus} annotation
* </ul>
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -70,36 +78,47 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
}
/**
* Invokes the method via {@link #invokeForRequest(NativeWebRequest, ModelMap, Object...)} and also handles the
* return value by invoking one of the {@link HandlerMethodReturnValueHandler} instances registered via
* {@link #setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite)}.
* If the method is annotated with {@link SessionStatus} the response status will be set.
* Invokes the method and handles the return value through registered {@link HandlerMethodReturnValueHandler}s.
* If the handler method is annotated with {@link ResponseStatus}, the status on the response is set accordingly
* after method invocation but before return value handling.
* <p>Return value handling may be skipped entirely if the handler method returns a {@code null} (or is a
* {@code void} method) and one of the following other conditions is true:
* <ul>
* <li>One of the {@link HandlerMethodArgumentResolver}s set the {@link ModelAndViewContainer#setResolveView(boolean)}
* flag to {@code false}. This is the case when a method argument allows the handler method access to the response.
* <li>The request qualifies as being not modified according to {@link ServletWebRequest#isNotModified()}.
* This is used in conjunction with a "Last-Modified" header or ETag.
* <li>The status on the response was set as a result of a {@link ResponseStatus} annotation
* </ul>
* <p>After the call, use the {@link ModelAndViewContainer} parameter to access model attributes and view selection
* and to determine if view resolution is needed.
*
* @param request the current request
* @param model the model used throughout the current request
* @param providedArgs argument values to use as-is if they match to a method parameter's type
* @return ModelAndView object with the name of the view and the required model data, or <code>null</code>
* if the response was handled
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param providedArgs argument values to try to use without the need for view resolution
*/
public final ModelAndView invokeAndHandle(NativeWebRequest request,
ModelMap model,
Object... providedArgs) throws Exception {
public final void invokeAndHandle(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object...providedArgs) throws Exception {
if (!returnValueHandlers.supportsReturnType(getReturnType())) {
throw new IllegalStateException("No suitable HandlerMethodReturnValueHandler for method " + toString());
}
Object returnValue = invokeForRequest(request, model, providedArgs);
Object returnValue = invokeForRequest(request, mavContainer, providedArgs);
setResponseStatus((ServletWebRequest) request);
if (returnValue == null && (isRequestNotModified(request) || usesResponseArgument())) {
return null;
if (returnValue == null) {
if (isRequestNotModified(request) || hasResponseStatus() || !mavContainer.isResolveView()) {
mavContainer.setResolveView(false);
return;
}
}
ModelAndViewContainer mavContainer = new ModelAndViewContainer(model);
returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
mavContainer.setResolveView(true);
return getModelAndView(request, mavContainer, returnValue);
returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
}
/**
@@ -120,38 +139,17 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
}
/**
* Create a {@link ModelAndView} from a {@link ModelAndViewContainer}.
* Does the request qualify as not modified?
*/
private ModelAndView getModelAndView(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object returnValue) {
if (returnValueHandlerUsesResponseArgument()) {
return null;
}
else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (mavContainer.getView() != null) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
private boolean isRequestNotModified(NativeWebRequest request) {
return ((ServletWebRequest) request).isNotModified();
}
/**
* Check whether the request qualifies as not modified...
* TODO: document fully including sample user code
* Does the method set the response status?
*/
private boolean isRequestNotModified(NativeWebRequest request) {
ServletWebRequest servletRequest = (ServletWebRequest) request;
return (servletRequest.isNotModified() || (responseStatus != null) || usesResponseArgument());
}
protected boolean usesResponseArgument() {
return (super.usesResponseArgument() || returnValueHandlerUsesResponseArgument() || (responseStatus != null));
private boolean hasResponseStatus() {
return responseStatus != null;
}
private boolean returnValueHandlerUsesResponseArgument() {
return returnValueHandlers.usesResponseArgument(getReturnType());
}
}
}

View File

@@ -68,7 +68,7 @@ public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValue
if (mav != ModelAndViewResolver.UNRESOLVED) {
mavContainer.setView(mav.getView());
mavContainer.setViewName(mav.getViewName());
mavContainer.addModelAttributes(mav.getModel());
mavContainer.addAllAttributes(mav.getModel());
return;
}
}
@@ -78,7 +78,7 @@ public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValue
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
mavContainer.addModelAttribute(name, returnValue);
mavContainer.addAttribute(name, returnValue);
}
// should not happen

View File

@@ -35,7 +35,6 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -74,7 +73,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
@@ -116,19 +115,25 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
mavContainer.setResolveView(false);
if (returnValue == null) {
return;
}
HttpOutputMessage outputMessage = createOutputMessage(webRequest);
Assert.isInstanceOf(HttpEntity.class, returnValue);
HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
HttpOutputMessage outputMessage = createOutputMessage(webRequest);
if (responseEntity instanceof ResponseEntity) {
((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode());
}
HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
outputMessage.getHeaders().putAll(entityHeaders);
}
Object body = responseEntity.getBody();
if (body != null) {
writeWithMessageConverters(body, createInputMessage(webRequest), outputMessage);

View File

@@ -46,10 +46,10 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
ModelAndView mav = (ModelAndView) returnValue;
mavContainer.setView(mav.getView());
mavContainer.setViewName(mav.getViewName());
mavContainer.addModelAttributes(mav.getModel());
mavContainer.addAllAttributes(mav.getModel());
}
else {
// TODO
mavContainer.setResolveView(false);
}
}

View File

@@ -27,7 +27,6 @@ import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.ui.ModelMap;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.annotation.RequestBody;
@@ -66,7 +65,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
@@ -82,8 +81,8 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException {
NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setResolveView(false);
if (returnValue != null) {
writeWithMessageConverters(webRequest, returnValue);
}

View File

@@ -27,10 +27,10 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.support.RequestContextUtils;
@@ -55,7 +55,7 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);

View File

@@ -24,10 +24,10 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link ServletResponse} and related arguments.
@@ -47,12 +47,14 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException {
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Class<?> parameterType = parameter.getParameterType();
mavContainer.setResolveView(false);
if (ServletResponse.class.isAssignableFrom(parameterType)) {
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
@@ -67,7 +69,9 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
else if (Writer.class.isAssignableFrom(parameterType)) {
return response.getWriter();
}
// should not happen
throw new UnsupportedOperationException();
else {
// should not happen
throw new UnsupportedOperationException();
}
}
}

View File

@@ -35,8 +35,6 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -49,8 +47,7 @@ import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
/**
@@ -106,10 +103,10 @@ public class ControllerMethodAnnotationDetectionTests {
NativeWebRequest webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
servletRequest.setParameter("name", "Chad");
ModelMap model = new ExtendedModelMap();
ModelAndView mav = requestMappingMethod.invokeAndHandle(webRequest, model);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
Object modelAttr = mav.getModelMap().get("attrName");
Object modelAttr = mavContainer.getAttribute("attrName");
assertEquals(TestBean.class, modelAttr.getClass());
assertEquals("Chad", ((TestBean) modelAttr).getName());

View File

@@ -28,6 +28,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
/**
@@ -56,7 +57,7 @@ public class ServletInvocableHandlerMethodTests {
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Handler(), method);
handlerMethod.setHandlerMethodReturnValueHandlers(handlers);
handlerMethod.invokeAndHandle(webRequest, null);
handlerMethod.invokeAndHandle(webRequest, new ModelAndViewContainer());
assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
assertEquals("400 Bad Request", response.getErrorMessage());

View File

@@ -46,6 +46,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
/**
@@ -65,16 +66,18 @@ public class HttpEntityMethodProcessorTests {
private MethodParameter intReturnValue;
private MethodParameter httpEntityReturnValue;
private MethodParameter intParameter;
private ModelAndViewContainer mavContainer;
private ServletWebRequest request;
private MockHttpServletRequest servletRequest;
private MockHttpServletResponse servletResponse;
private MethodParameter httpEntityReturnValue;
private MethodParameter intParameter;
@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
@@ -93,6 +96,8 @@ public class HttpEntityMethodProcessorTests {
Method other = getClass().getMethod("otherMethod");
intReturnValue = new MethodParameter(other, -1);
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest();
servletResponse = new MockHttpServletResponse();
request = new ServletWebRequest(servletRequest, servletResponse);
@@ -135,7 +140,7 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, null, request, null);
HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, mavContainer, request, null);
assertEquals("Invalid argument", expected, result.getBody());
verify(messageConverter);
@@ -152,14 +157,14 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
processor.resolveArgument(httpEntityParam, null, request, null);
processor.resolveArgument(httpEntityParam, mavContainer, request, null);
verify(messageConverter);
}
@Test(expected = HttpMediaTypeNotSupportedException.class)
public void resolveArgumentNoContentType() throws Exception {
processor.resolveArgument(httpEntityParam, null, request, null);
processor.resolveArgument(httpEntityParam, mavContainer, request, null);
}
@Test
@@ -175,8 +180,9 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
@@ -193,8 +199,9 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
@@ -205,8 +212,9 @@ public class HttpEntityMethodProcessorTests {
ResponseEntity<String> returnValue = new ResponseEntity<String>(responseHeaders, HttpStatus.ACCEPTED);
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(new StringHttpMessageConverter());
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
assertEquals("headerValue", servletResponse.getHeader("header"));
}
@@ -217,8 +225,9 @@ public class HttpEntityMethodProcessorTests {
ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(new StringHttpMessageConverter());
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
assertEquals("headerValue", servletResponse.getHeader("header"));
}

View File

@@ -44,6 +44,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
/**
@@ -63,6 +64,8 @@ public class RequestResponseBodyMethodProcessorTests {
private MethodParameter intReturnValue;
private ModelAndViewContainer mavContainer;
private NativeWebRequest webRequest;
private MockHttpServletRequest servletRequest;
@@ -79,6 +82,8 @@ public class RequestResponseBodyMethodProcessorTests {
Method other = getClass().getMethod("otherMethod");
intReturnValue = new MethodParameter(other, -1);
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest();
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
webRequest = new ServletWebRequest(servletRequest, servletResponse);
@@ -116,7 +121,7 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
Object result = processor.resolveArgument(stringParameter, null, webRequest, null);
Object result = processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
assertEquals("Invalid argument", expected, result);
verify(messageConverter);
@@ -134,14 +139,14 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
processor.resolveArgument(stringParameter, null, webRequest, null);
processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
verify(messageConverter);
}
@Test(expected = HttpMediaTypeNotSupportedException.class)
public void resolveArgumentNoContentType() throws Exception {
processor.resolveArgument(stringParameter, null, webRequest, null);
processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
}
@Test
@@ -156,8 +161,9 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, stringReturnValue, null, webRequest);
processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
@@ -173,8 +179,9 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, stringReturnValue, null, webRequest);
processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}

View File

@@ -28,6 +28,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
import static org.junit.Assert.*;
@@ -45,12 +46,14 @@ public class ServletResponseMethodArgumentResolverTests {
private MockHttpServletResponse servletResponse;
private ModelAndViewContainer mavContainer;
@Before
public void setUp() throws Exception {
resolver = new ServletResponseMethodArgumentResolver();
supportedParams =
getClass().getMethod("supportedParams", ServletResponse.class, OutputStream.class, Writer.class);
supportedParams = getClass().getMethod("supportedParams", ServletResponse.class, OutputStream.class, Writer.class);
servletResponse = new MockHttpServletResponse();
mavContainer = new ModelAndViewContainer();
webRequest = new ServletWebRequest(new MockHttpServletRequest(), servletResponse);
}
@@ -65,8 +68,9 @@ public class ServletResponseMethodArgumentResolverTests {
assertTrue("ServletResponse not supported", resolver.supportsParameter(servletResponseParameter));
Object result = resolver.resolveArgument(servletResponseParameter, null, webRequest, null);
Object result = resolver.resolveArgument(servletResponseParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse, result);
assertFalse(mavContainer.isResolveView());
}
@Test
@@ -75,8 +79,9 @@ public class ServletResponseMethodArgumentResolverTests {
assertTrue("OutputStream not supported", resolver.supportsParameter(outputStreamParameter));
Object result = resolver.resolveArgument(outputStreamParameter, null, webRequest, null);
Object result = resolver.resolveArgument(outputStreamParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse.getOutputStream(), result);
assertFalse(mavContainer.isResolveView());
}
@Test
@@ -85,8 +90,9 @@ public class ServletResponseMethodArgumentResolverTests {
assertTrue("Writer not supported", resolver.supportsParameter(writerParameter));
Object result = resolver.resolveArgument(writerParameter, null, webRequest, null);
Object result = resolver.resolveArgument(writerParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse.getWriter(), result);
assertFalse(mavContainer.isResolveView());
}
public void supportedParams(ServletResponse p0, OutputStream p1, Writer p2) {