@Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new ModelAttributeMethodProcessor(true)); }
attribute = createAttribute(name, parameter, binderFactory, webRequest); if (isBindExceptionRequired(parameter)) { if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult());
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (this.mavResolvers != null) { for (ModelAndViewResolver mavResolver : this.mavResolvers) { Class<?> handlerType = returnType.getContainingClass(); Method method = returnType.getMethod(); Assert.state(method != null, "No handler method"); ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel(); ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, webRequest); if (mav != ModelAndViewResolver.UNRESOLVED) { mavContainer.addAllAttributes(mav.getModel()); mavContainer.setViewName(mav.getViewName()); if (!mav.isReference()) { mavContainer.setView(mav.getView()); } return; } } } // No suitable ModelAndViewResolver... if (this.modelAttributeProcessor.supportsReturnType(returnType)) { this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } else { throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } }
@Test public void supportedReturnTypes() throws Exception { processor = new ModelAttributeMethodProcessor(false); assertTrue(this.processor.supportsReturnType(returnParamNamedModelAttr)); assertFalse(this.processor.supportsReturnType(returnParamNonSimpleType)); }
@Test public void supportedParametersInDefaultResolutionMode() throws Exception { processor = new ModelAttributeMethodProcessor(true); // Only non-simple types, even if not annotated assertTrue(this.processor.supportsParameter(this.paramNamedValidModelAttr)); assertTrue(this.processor.supportsParameter(this.paramErrors)); assertTrue(this.processor.supportsParameter(this.paramModelAttr)); assertTrue(this.processor.supportsParameter(this.paramNonSimpleType)); assertFalse(this.processor.supportsParameter(this.paramInt)); }
/** * Instantiate the model attribute from a URI template variable or from a * request parameter if the name matches to the model attribute name and * if there is an appropriate type conversion strategy. If none of these * are true delegate back to the base class. * @see #createAttributeFromRequestValue */ @Override protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { String value = getRequestValueForAttribute(attributeName, request); if (value != null) { Object attribute = createAttributeFromRequestValue( value, attributeName, parameter, binderFactory, request); if (attribute != null) { return attribute; } } return super.createAttribute(attributeName, parameter, binderFactory, request); }
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception { Object constructed = constructAttribute(ctor, attributeName, binderFactory, webRequest); if (constructed != null) { return constructed; Object value = args[i]; result.recordFieldValue(paramName, paramTypes[i], value); validateValueIfApplicable(binder, parameter, ctor.getDeclaringClass(), paramName, value);
/** * Whether to raise a fatal bind exception on validation errors. * <p>The default implementation delegates to {@link #isBindExceptionRequired(MethodParameter)}. * @param binder the data binder used to perform data binding * @param parameter the method parameter declaration * @return {@code true} if the next method parameter is not of type {@link Errors} * @see #isBindExceptionRequired(MethodParameter) */ protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) { return isBindExceptionRequired(parameter); }
/** * Validate the model attribute if applicable. * <p>The default implementation checks for {@code @javax.validation.Valid}, * Spring's {@link org.springframework.validation.annotation.Validated}, * and custom annotations whose name starts with "Valid". * @param binder the DataBinder to be used * @param parameter the method parameter declaration * @see WebDataBinder#validate(Object...) * @see SmartValidator#validate(Object, Errors, Object...) */ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { for (Annotation ann : parameter.getParameterAnnotations()) { Object[] validationHints = determineValidationHints(ann); if (validationHints != null) { binder.validate(validationHints); break; } } }
Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest); if (parameter != nestedParameter) { attribute = Optional.of(attribute);
@Test public void handleAnnotatedReturnValue() throws Exception { this.processor.handleReturnValue("expected", this.returnParamNamedModelAttr, this.container, this.request); assertEquals("expected", this.container.getModel().get("modelAttrName")); }
@Test public void supportedReturnTypesInDefaultResolutionMode() throws Exception { processor = new ModelAttributeMethodProcessor(true); assertTrue(this.processor.supportsReturnType(returnParamNamedModelAttr)); assertTrue(this.processor.supportsReturnType(returnParamNonSimpleType)); }
/** * Instantiate the model attribute from a URI template variable or from a * request parameter if the name matches to the model attribute name and * if there is an appropriate type conversion strategy. If none of these * are true delegate back to the base class. * @see #createAttributeFromRequestValue */ @Override protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { String value = getRequestValueForAttribute(attributeName, request); if (value != null) { Object attribute = createAttributeFromRequestValue( value, attributeName, parameter, binderFactory, request); if (attribute != null) { return attribute; } } return super.createAttribute(attributeName, parameter, binderFactory, request); }
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception { Object constructed = constructAttribute(ctor, attributeName, binderFactory, webRequest); if (constructed != null) { return constructed; Object value = args[i]; result.recordFieldValue(paramName, paramTypes[i], value); validateValueIfApplicable(binder, parameter, ctor.getDeclaringClass(), paramName, value);
/** * Whether to raise a fatal bind exception on validation errors. * <p>The default implementation delegates to {@link #isBindExceptionRequired(MethodParameter)}. * @param binder the data binder used to perform data binding * @param parameter the method parameter declaration * @return {@code true} if the next method parameter is not of type {@link Errors} * @see #isBindExceptionRequired(MethodParameter) */ protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) { return isBindExceptionRequired(parameter); }
/** * Validate the model attribute if applicable. * <p>The default implementation checks for {@code @javax.validation.Valid}, * Spring's {@link org.springframework.validation.annotation.Validated}, * and custom annotations whose name starts with "Valid". * @param binder the DataBinder to be used * @param parameter the method parameter declaration * @see WebDataBinder#validate(Object...) * @see SmartValidator#validate(Object, Errors, Object...) */ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { for (Annotation ann : parameter.getParameterAnnotations()) { Object[] validationHints = determineValidationHints(ann); if (validationHints != null) { binder.validate(validationHints); break; } } }
Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest); if (parameter != nestedParameter) { attribute = Optional.of(attribute);
@Test public void handleNotAnnotatedReturnValue() throws Exception { TestBean testBean = new TestBean("expected"); this.processor.handleReturnValue(testBean, this.returnParamNonSimpleType, this.container, this.request); assertSame(testBean, this.container.getModel().get("testBean")); }
attribute = createAttribute(name, parameter, binderFactory, webRequest); if (isBindExceptionRequired(parameter)) { if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult());
@Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { returnValueHandlers.add(new ModelAttributeMethodProcessor(true)); }