/** * Resolve the argument from the model or if not found instantiate it with its default if it is * available. The model attribute is then populated with request values via data binding and * optionally validated if {@code @java.validation.Valid} is present on the argument. * * @throws org.springframework.validation.BindException if data binding and validation result in * an error and the next method parameter is not of type {@link * org.springframework.validation.Errors}. * @throws Exception if WebDataBinder initialization fails. */ public final Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { String name = parameter.getParameterAnnotation(FormModel.class).value(); Object target = (mavContainer.containsAttribute(name)) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request); WebDataBinder binder = binderFactory.createBinder(request, target, name); target = binder.getTarget(); if (target != null) { bindRequestParameters(mavContainer, binderFactory, binder, request, parameter); validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors()) { if (isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } } target = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType()); mavContainer.addAttribute(name, target); return target; }
/** * Resolve the argument from the model or if not found instantiate it with its default if it is * available. The model attribute is then populated with request values via data binding and * optionally validated if {@code @java.validation.Valid} is present on the argument. * * @throws BindException if data binding and validation result in an error and the next method * parameter is not of type {@link Errors}. * @throws Exception if WebDataBinder initialization fails. */ @Override public final Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String name = ModelFactory.getNameForParameter(parameter); Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest)); WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { bindRequestParameters(binder, webRequest); validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors()) { if (isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } } // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = binder.getBindingResult().getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); }
private void doBind( WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors) throws Exception { doBind(binder, webRequest); if (validate) { binder.validate(); } if (failOnErrors && binder.getBindingResult().hasErrors()) { throw new BindException(binder.getBindingResult()); } }
protected void validateComponent(WebDataBinder binder, MethodParameter parameter) throws BindException { boolean validateParameter = validateParameter(parameter); Annotation[] annotations = binder.getTarget().getClass().getAnnotations(); for (Annotation annot : annotations) { if (annot.annotationType().getSimpleName().startsWith("Valid") && validateParameter) { Object hints = AnnotationUtils.getValue(annot); binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); } } if (binder.getBindingResult().hasErrors()) { if (isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } }
private ModelAndView getModelAndViewSettingsPage( Map<String, String> parameters, SettingsForm settingsForm, MockHttpServletRequest request) { if (settingsForm == null) { settingsForm = new SettingsForm(); } WebDataBinder binder = new WebDataBinder(settingsForm, "settingsform"); if (parameters != null) { request.setParameters(parameters); binder.bind(new MutablePropertyValues(request.getParameterMap())); } SessionStatus status = new SimpleSessionStatus(); return controller.submitSettingsPage(settingsForm, binder.getBindingResult(), status, request); }
private void validate(WebDataBinder binder, MethodParameter parameter) throws MethodArgumentNotValidException { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation annot : annotations) { if (annot.annotationType().getSimpleName().startsWith("Valid")) { Object hints = AnnotationUtils.getValue(annot); binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); BindingResult bindingResult = binder.getBindingResult(); if (bindingResult.hasErrors()) { if (isBindingErrorFatal(parameter)) { throw new MethodArgumentNotValidException(parameter, bindingResult); } } } } }
public final void updateModelAttributes( Object handler, Map<String, Object> mavModel, ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception { if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) { for (String attrName : this.methodResolver.getActualSessionAttributeNames()) { this.sessionAttributeStore.cleanupAttribute(webRequest, attrName); } } // Expose model attributes as session attributes, if required. // Expose BindingResults for all attributes, making custom editors available. Map<String, Object> model = (mavModel != null ? mavModel : implicitModel); if (model != null) { try { String[] originalAttrNames = model.keySet().toArray(new String[model.size()]); for (String attrName : originalAttrNames) { Object attrValue = model.get(attrName); boolean isSessionAttr = this.methodResolver.isSessionAttribute( attrName, (attrValue != null ? attrValue.getClass() : null)); if (isSessionAttr) { if (this.sessionStatus.isComplete()) { implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE); } else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) { this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue); } } if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) && (isSessionAttr || isBindingCandidate(attrValue))) { String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName; if (mavModel != null && !model.containsKey(bindingResultKey)) { WebDataBinder binder = createBinder(webRequest, attrValue, attrName); initBinder(handler, attrName, binder, webRequest); mavModel.put(bindingResultKey, binder.getBindingResult()); } } } } catch (InvocationTargetException ex) { // User-defined @InitBinder method threw an exception... ReflectionUtils.rethrowException(ex.getTargetException()); } } }
public Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Object arg = readWithMessageConverters(webRequest, parameter, parameter.getParameterType()); if (shouldValidate(parameter, arg)) { String argName = Conventions.getVariableNameForParameter(parameter); WebDataBinder binder = binderFactory.createBinder(webRequest, arg, argName); binder.validate(); Errors errors = binder.getBindingResult(); if (errors.hasErrors()) { throw new RequestBodyNotValidException(errors); } } return arg; }
@SuppressWarnings("unchecked") public final void updateModelAttributes( Object handler, Map<String, Object> mavModel, ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception { if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) { for (String attrName : this.methodResolver.getActualSessionAttributeNames()) { this.sessionAttributeStore.cleanupAttribute(webRequest, attrName); } } // Expose model attributes as session attributes, if required. // Expose BindingResults for all attributes, making custom editors available. Map<String, Object> model = (mavModel != null ? mavModel : implicitModel); for (String attrName : new HashSet<String>(model.keySet())) { Object attrValue = model.get(attrName); boolean isSessionAttr = this.methodResolver.isSessionAttribute( attrName, (attrValue != null ? attrValue.getClass() : null)); if (isSessionAttr && !this.sessionStatus.isComplete()) { this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue); } if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) && (isSessionAttr || isBindingCandidate(attrValue))) { String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName; if (mavModel != null && !model.containsKey(bindingResultKey)) { WebDataBinder binder = createBinder(webRequest, attrValue, attrName); initBinder(handler, attrName, binder, webRequest); mavModel.put(bindingResultKey, binder.getBindingResult()); } } } }
/** * {@inheritDoc} * * <p>Downcast {@link org.springframework.web.bind.WebDataBinder} to {@link * org.springframework.web.bind.ServletRequestDataBinder} before binding. * * @throws Exception * @see org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory */ protected void bindRequestParameters( ModelAndViewContainer mavContainer, WebDataBinderFactory binderFactory, WebDataBinder binder, NativeWebRequest request, MethodParameter parameter) throws Exception { Map<String, Boolean> hasProcessedPrefixMap = new HashMap<String, Boolean>(); Class<?> targetType = binder.getTarget().getClass(); ServletRequest servletRequest = prepareServletRequest(binder.getTarget(), request, parameter); WebDataBinder simpleBinder = binderFactory.createBinder(request, null, null); if (Collection.class.isAssignableFrom(targetType)) { // bind collection or array Type type = parameter.getGenericParameterType(); Class<?> componentType = Object.class; Collection target = (Collection) binder.getTarget(); List targetList = new ArrayList(target); if (type instanceof ParameterizedType) { componentType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0]; } if (parameter.getParameterType().isArray()) { componentType = parameter.getParameterType().getComponentType(); } for (Object key : servletRequest.getParameterMap().keySet()) { String prefixName = getPrefixName((String) key); // 每个prefix 只处理一次 if (hasProcessedPrefixMap.containsKey(prefixName)) { continue; } else { hasProcessedPrefixMap.put(prefixName, Boolean.TRUE); } if (isSimpleComponent(prefixName)) { // bind simple type Map<String, Object> paramValues = WebUtils.getParametersStartingWith(servletRequest, prefixName); Matcher matcher = INDEX_PATTERN.matcher(prefixName); if (!matcher.matches()) { // 处理如 array=1&array=2的情况 for (Object value : paramValues.values()) { targetList.add(simpleBinder.convertIfNecessary(value, componentType)); } } else { // 处理如 array[0]=1&array[1]=2的情况 int index = Integer.valueOf(matcher.group(1)); if (targetList.size() <= index) { growCollectionIfNecessary(targetList, index); } targetList.set( index, simpleBinder.convertIfNecessary(paramValues.values(), componentType)); } } else { // 处理如 // votes[1].title=votes[1].title&votes[0].title=votes[0].title&votes[0].id=0&votes[1].id=1 Object component = null; // 先查找老的 即已经在集合中的数据(而不是新添加一个) Matcher matcher = INDEX_PATTERN.matcher(prefixName); if (!matcher.matches()) { throw new IllegalArgumentException( "bind collection error, need integer index, key:" + key); } int index = Integer.valueOf(matcher.group(1)); if (targetList.size() <= index) { growCollectionIfNecessary(targetList, index); } Iterator iterator = targetList.iterator(); for (int i = 0; i < index; i++) { iterator.next(); } component = iterator.next(); if (component == null) { component = BeanUtils.instantiate(componentType); } WebDataBinder componentBinder = binderFactory.createBinder(request, component, null); component = componentBinder.getTarget(); if (component != null) { ServletRequestParameterPropertyValues pvs = new ServletRequestParameterPropertyValues(servletRequest, prefixName, ""); componentBinder.bind(pvs); validateIfApplicable(componentBinder, parameter); if (componentBinder.getBindingResult().hasErrors()) { if (isBindExceptionRequired(componentBinder, parameter)) { throw new BindException(componentBinder.getBindingResult()); } } targetList.set(index, component); } } target.clear(); target.addAll(targetList); } } else if (MapWapper.class.isAssignableFrom(targetType)) { Type type = parameter.getGenericParameterType(); Class<?> keyType = Object.class; Class<?> valueType = Object.class; if (type instanceof ParameterizedType) { keyType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0]; valueType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[1]; } MapWapper mapWapper = ((MapWapper) binder.getTarget()); Map target = mapWapper.getInnerMap(); if (target == null) { target = new HashMap(); mapWapper.setInnerMap(target); } for (Object key : servletRequest.getParameterMap().keySet()) { String prefixName = getPrefixName((String) key); // 每个prefix 只处理一次 if (hasProcessedPrefixMap.containsKey(prefixName)) { continue; } else { hasProcessedPrefixMap.put(prefixName, Boolean.TRUE); } Object keyValue = simpleBinder.convertIfNecessary(getMapKey(prefixName), keyType); if (isSimpleComponent(prefixName)) { // bind simple type Map<String, Object> paramValues = WebUtils.getParametersStartingWith(servletRequest, prefixName); for (Object value : paramValues.values()) { target.put(keyValue, simpleBinder.convertIfNecessary(value, valueType)); } } else { Object component = target.get(keyValue); if (component == null) { component = BeanUtils.instantiate(valueType); } WebDataBinder componentBinder = binderFactory.createBinder(request, component, null); component = componentBinder.getTarget(); if (component != null) { ServletRequestParameterPropertyValues pvs = new ServletRequestParameterPropertyValues(servletRequest, prefixName, ""); componentBinder.bind(pvs); validateComponent(componentBinder, parameter); target.put(keyValue, component); } } } } else { // bind model ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; servletBinder.bind(servletRequest); } }
@SuppressWarnings("unchecked") private Object[] resolveHandlerArguments( Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception { Class[] paramTypes = handlerMethod.getParameterTypes(); Object[] args = new Object[paramTypes.length]; for (int i = 0; i < args.length; i++) { MethodParameter methodParam = new MethodParameter(handlerMethod, i); methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(methodParam, handler.getClass()); String paramName = null; String headerName = null; boolean requestBodyFound = false; String cookieName = null; String pathVarName = null; String attrName = null; boolean required = false; String defaultValue = null; boolean validate = false; int found = 0; Annotation[] paramAnns = methodParam.getParameterAnnotations(); for (Annotation paramAnn : paramAnns) { if (RequestParam.class.isInstance(paramAnn)) { RequestParam requestParam = (RequestParam) paramAnn; paramName = requestParam.value(); required = requestParam.required(); defaultValue = requestParam.defaultValue(); found++; } else if (RequestHeader.class.isInstance(paramAnn)) { RequestHeader requestHeader = (RequestHeader) paramAnn; headerName = requestHeader.value(); required = requestHeader.required(); defaultValue = requestHeader.defaultValue(); found++; } else if (RequestBody.class.isInstance(paramAnn)) { requestBodyFound = true; found++; } else if (CookieValue.class.isInstance(paramAnn)) { CookieValue cookieValue = (CookieValue) paramAnn; cookieName = cookieValue.value(); required = cookieValue.required(); defaultValue = cookieValue.defaultValue(); found++; } else if (PathVariable.class.isInstance(paramAnn)) { PathVariable pathVar = (PathVariable) paramAnn; pathVarName = pathVar.value(); found++; } else if (ModelAttribute.class.isInstance(paramAnn)) { ModelAttribute attr = (ModelAttribute) paramAnn; attrName = attr.value(); found++; } else if (Value.class.isInstance(paramAnn)) { defaultValue = ((Value) paramAnn).value(); } else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) { validate = true; } } if (found > 1) { throw new IllegalStateException( "Handler parameter annotations are exclusive choices - " + "do not specify more than one such annotation on the same parameter: " + handlerMethod); } if (found == 0) { Object argValue = resolveCommonArgument(methodParam, webRequest); if (argValue != WebArgumentResolver.UNRESOLVED) { args[i] = argValue; } else if (defaultValue != null) { args[i] = resolveDefaultValue(defaultValue); } else { Class paramType = methodParam.getParameterType(); if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) { args[i] = implicitModel; } else if (SessionStatus.class.isAssignableFrom(paramType)) { args[i] = this.sessionStatus; } else if (Errors.class.isAssignableFrom(paramType)) { throw new IllegalStateException( "Errors/BindingResult argument declared " + "without preceding model attribute. Check your handler method signature!"); } else if (BeanUtils.isSimpleProperty(paramType)) { paramName = ""; } else { attrName = ""; } } } if (paramName != null) { args[i] = resolveRequestParam( paramName, required, defaultValue, methodParam, webRequest, handler); } else if (headerName != null) { args[i] = resolveRequestHeader( headerName, required, defaultValue, methodParam, webRequest, handler); } else if (requestBodyFound) { args[i] = resolveRequestBody(methodParam, webRequest, handler); } else if (cookieName != null) { args[i] = resolveCookieValue( cookieName, required, defaultValue, methodParam, webRequest, handler); } else if (pathVarName != null) { args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler); } else if (attrName != null) { WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler); boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1])); if (binder.getTarget() != null) { doBind(binder, webRequest, validate, !assignBindingResult); } args[i] = binder.getTarget(); if (assignBindingResult) { args[i + 1] = binder.getBindingResult(); i++; } implicitModel.putAll(binder.getBindingResult().getModel()); } } return args; }
@Override public Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); assertIsMultipartRequest(servletRequest); MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class); String partName = getPartName(parameter); Object arg; if (MultipartFile.class.equals(parameter.getParameterType())) { Assert.notNull( multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); arg = multipartRequest.getFile(partName); } else if (isMultipartFileCollection(parameter)) { Assert.notNull( multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); arg = multipartRequest.getFiles(partName); } else if (isMultipartFileArray(parameter)) { Assert.notNull( multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); List<MultipartFile> files = multipartRequest.getFiles(partName); arg = files.toArray(new MultipartFile[files.size()]); } else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) { assertIsMultipartRequest(servletRequest); arg = servletRequest.getPart(partName); } else if (isPartCollection(parameter)) { assertIsMultipartRequest(servletRequest); arg = new ArrayList<Object>(servletRequest.getParts()); } else if (isPartArray(parameter)) { assertIsMultipartRequest(servletRequest); arg = RequestPartResolver.resolvePart(servletRequest); } else { try { HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, partName); arg = readWithMessageConverters(inputMessage, parameter, parameter.getParameterType()); WebDataBinder binder = binderFactory.createBinder(request, arg, partName); if (arg != null) { validate(binder, parameter); } mavContainer.addAttribute( BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult()); } catch (MissingServletRequestPartException ex) { // handled below arg = null; } } RequestPart annot = parameter.getParameterAnnotation(RequestPart.class); boolean isRequired = (annot == null || annot.required()); if (arg == null && isRequired) { throw new MissingServletRequestPartException(partName); } return arg; }