diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 5ece3bbc877..7057743d4b2 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -70,6 +70,7 @@ import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.validation.support.BindingAwareModelMap; +import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @@ -87,6 +88,7 @@ import org.springframework.web.context.request.RequestScope; import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.bind.MissingPortletRequestParameterException; +import org.springframework.web.portlet.bind.PortletRequestDataBinder; import org.springframework.web.portlet.bind.annotation.ActionMapping; import org.springframework.web.portlet.bind.annotation.EventMapping; import org.springframework.web.portlet.bind.annotation.RenderMapping; @@ -212,7 +214,7 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator } /** - * Set a custom WebArgumentResolvers to use for special method parameter types. + * Set a custom WebArgumentResolver to use for special method parameter types. * Such a custom WebArgumentResolver will kick in first, having a chance to * resolve an argument value before the standard argument handling kicks in. */ @@ -379,6 +381,26 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator } + /** + * Template method for creating a new PortletRequestDataBinder instance. + *

The default implementation creates a standard PortletRequestDataBinder. + * This can be overridden for custom PortletRequestDataBinder subclasses. + * @param request current portlet request + * @param target the target object to bind onto (or null + * if the binder is just used to convert a plain parameter value) + * @param objectName the objectName of the target object + * @return the PortletRequestDataBinder instance to use + * @throws Exception in case of invalid state or arguments + * @see PortletRequestDataBinder#bind(javax.portlet.PortletRequest) + * @see PortletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter) + */ + protected PortletRequestDataBinder createBinder( + PortletRequest request, Object target, String objectName) throws Exception { + + return new PortletRequestDataBinder(target, objectName); + } + + /** * Portlet-specific subclass of {@link HandlerMethodResolver}. */ @@ -507,6 +529,20 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator throw new PortletSessionRequiredException(message); } + @Override + protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) + throws Exception { + + return AnnotationMethodHandlerAdapter.this.createBinder( + (PortletRequest) webRequest.getNativeRequest(), target, objectName); + } + + @Override + protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception { + PortletRequestDataBinder portletBinder = (PortletRequestDataBinder) binder; + portletBinder.bind((PortletRequest) webRequest.getNativeRequest()); + } + @Override protected Object resolveDefaultValue(String value) { if (beanFactory == null) { @@ -610,8 +646,8 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator // Invoke custom resolvers if present... if (customModelAndViewResolvers != null) { for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) { - org.springframework.web.servlet.ModelAndView smav = mavResolver - .resolveModelAndView(handlerMethod, handlerType, returnValue, implicitModel, webRequest); + org.springframework.web.servlet.ModelAndView smav = + mavResolver.resolveModelAndView(handlerMethod, handlerType, returnValue, implicitModel, webRequest); if (smav != ModelAndViewResolver.UNRESOLVED) { return (smav.isReference() ? new ModelAndView(smav.getViewName(), smav.getModelMap()) : diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index ef5e45fa9a8..fea02c54fbc 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -80,6 +80,8 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpSessionRequiredException; import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.ServletRequestDataBinder; +import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @@ -434,6 +436,26 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator } + /** + * Template method for creating a new ServletRequestDataBinder instance. + *

The default implementation creates a standard ServletRequestDataBinder. + * This can be overridden for custom ServletRequestDataBinder subclasses. + * @param request current HTTP request + * @param target the target object to bind onto (or null + * if the binder is just used to convert a plain parameter value) + * @param objectName the objectName of the target object + * @return the ServletRequestDataBinder instance to use + * @throws Exception in case of invalid state or arguments + * @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest) + * @see ServletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter) + */ + protected ServletRequestDataBinder createBinder( + HttpServletRequest request, Object target, String objectName) throws Exception { + + return new ServletRequestDataBinder(target, objectName); + } + + /** * Servlet-specific subclass of {@link HandlerMethodResolver}. */ @@ -554,7 +576,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator /** * Determines the matched pattern for the given methodLevelPattern and path. - * *

Uses the following algorithm:

  1. If there is a type-level mapping with path information, it is {@linkplain * PathMatcher#combine(String, String) combined} with the method-level pattern.
  2. If there is a {@linkplain * HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} in the request, it is combined with the @@ -661,6 +682,26 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator throw new HttpSessionRequiredException(message); } + @Override + protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) + throws Exception { + + return AnnotationMethodHandlerAdapter.this.createBinder( + (HttpServletRequest) webRequest.getNativeRequest(), target, objectName); + } + + @Override + protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception { + ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; + servletBinder.bind((ServletRequest) webRequest.getNativeRequest()); + } + + @Override + protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { + HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest(); + return new ServletServerHttpRequest(servletRequest); + } + @Override protected Object resolveDefaultValue(String value) { if (beanFactory == null) { @@ -674,12 +715,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator return exprResolver.evaluate(placeholdersResolved, expressionContext); } - @Override - protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { - HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest(); - return new ServletServerHttpRequest(servletRequest); - } - @Override protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest) throws Exception { diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index 92715fe8842..d7d2b247537 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -51,6 +51,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; +import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.HttpMediaTypeNotSupportedException; @@ -287,7 +288,7 @@ public class HandlerMethodInvoker { args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler); } else if (attrName != null) { - WebRequestDataBinder binder = + WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler); boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1])); if (binder.getTarget() != null) { @@ -433,7 +434,7 @@ public class HandlerMethodInvoker { } paramValue = checkValue(paramName, paramValue, paramType); } - WebRequestDataBinder binder = new WebRequestDataBinder(null, paramName); + WebDataBinder binder = createBinder(webRequest, null, paramName); initBinder(handlerForInitBinderCall, paramName, binder, webRequest); return binder.convertIfNecessary(paramValue, paramType, methodParam); } @@ -486,7 +487,7 @@ public class HandlerMethodInvoker { } headerValue = checkValue(headerName, headerValue, paramType); } - WebRequestDataBinder binder = new WebRequestDataBinder(null, headerName); + WebDataBinder binder = createBinder(webRequest, null, headerName); initBinder(handlerForInitBinderCall, headerName, binder, webRequest); return binder.convertIfNecessary(headerValue, paramType, methodParam); } @@ -551,20 +552,9 @@ public class HandlerMethodInvoker { throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes); } - /** - * Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}. - *

    Throws an UnsupportedOperationException by default. - */ - protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { - throw new UnsupportedOperationException("@RequestBody not supported"); - } - - private Object resolveCookieValue(String cookieName, - boolean required, - String defaultValue, - MethodParameter methodParam, - NativeWebRequest webRequest, - Object handlerForInitBinderCall) throws Exception { + private Object resolveCookieValue(String cookieName, boolean required, String defaultValue, + MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall) + throws Exception { Class paramType = methodParam.getParameterType(); if (cookieName.length() == 0) { @@ -580,7 +570,7 @@ public class HandlerMethodInvoker { } cookieValue = checkValue(cookieName, cookieValue, paramType); } - WebRequestDataBinder binder = new WebRequestDataBinder(null, cookieName); + WebDataBinder binder = createBinder(webRequest, null, cookieName); initBinder(handlerForInitBinderCall, cookieName, binder, webRequest); return binder.convertIfNecessary(cookieValue, paramType, methodParam); } @@ -595,17 +585,15 @@ public class HandlerMethodInvoker { throw new UnsupportedOperationException("@CookieValue not supported"); } - private Object resolvePathVariable(String pathVarName, - MethodParameter methodParam, - NativeWebRequest webRequest, - Object handlerForInitBinderCall) throws Exception { + private Object resolvePathVariable(String pathVarName, MethodParameter methodParam, + NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception { Class paramType = methodParam.getParameterType(); if (pathVarName.length() == 0) { pathVarName = getRequiredParameterName(methodParam); } String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest); - WebRequestDataBinder binder = new WebRequestDataBinder(null, pathVarName); + WebDataBinder binder = createBinder(webRequest, null, pathVarName); initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest); return binder.convertIfNecessary(pathVarValue, paramType, methodParam); } @@ -644,7 +632,7 @@ public class HandlerMethodInvoker { return value; } - private WebRequestDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam, + private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam, ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception { // Bind request parameter onto object... @@ -666,7 +654,7 @@ public class HandlerMethodInvoker { else { bindObject = BeanUtils.instantiateClass(paramType); } - WebRequestDataBinder binder = new WebRequestDataBinder(bindObject, name); + WebDataBinder binder = createBinder(webRequest, bindObject, name); initBinder(handler, name, binder, webRequest); return binder; } @@ -695,7 +683,7 @@ public class HandlerMethodInvoker { (isSessionAttr || isBindingCandidate(attrValue))) { String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName; if (mavModel != null && !model.containsKey(bindingResultKey)) { - WebRequestDataBinder binder = new WebRequestDataBinder(attrValue, attrName); + WebDataBinder binder = createBinder(webRequest, attrValue, attrName); initBinder(handler, attrName, binder, webRequest); mavModel.put(bindingResultKey, binder.getBindingResult()); } @@ -740,18 +728,36 @@ public class HandlerMethodInvoker { throw new IllegalStateException(message); } - protected void doBind(WebRequestDataBinder binder, NativeWebRequest webRequest, boolean validate, - boolean failOnErrors) throws Exception { + protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) + throws Exception { - binder.bind(webRequest); + return new WebRequestDataBinder(target, objectName); + } + + private void doBind(WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors) + throws Exception { + + doBind(binder, webRequest); if (validate) { binder.validate(); } - if (failOnErrors) { - binder.closeNoCatch(); + if (failOnErrors && binder.getBindingResult().hasErrors()) { + throw new BindException(binder.getBindingResult()); } } + protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception { + ((WebRequestDataBinder) binder).bind(webRequest); + } + + /** + * Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}. + *

    Throws an UnsupportedOperationException by default. + */ + protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { + throw new UnsupportedOperationException("@RequestBody not supported"); + } + protected Object resolveDefaultValue(String value) { return value; }