private ModelAttributeMethodArgumentResolver createResolver() { return new ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry.getSharedInstance(), false); }
private Mono<?> prepareAttributeMono(String attributeName, ResolvableType attributeType, BindingContext context, ServerWebExchange exchange) { Object attribute = context.getModel().asMap().get(attributeName); if (attribute == null) { attribute = findAndRemoveReactiveAttribute(context.getModel(), attributeName); } if (attribute == null) { return createAttribute(attributeName, attributeType.toClass(), context, exchange); } ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute); if (adapterFrom != null) { Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding only supports single-value async types"); return Mono.from(adapterFrom.toPublisher(attribute)); } else { return Mono.justOrEmpty(attribute); } }
@Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { return true; } else if (this.useDefaultResolution) { return checkParameterType(parameter, type -> !BeanUtils.isSimpleProperty(type)); } return false; }
ReactiveAdapter adapter = (resolvedType != null ? getAdapterRegistry().getAdapter(resolvedType) : null); ResolvableType valueType = (adapter != null ? type.getGeneric() : type); Mono<?> valueMono = prepareAttributeMono(name, valueType, context, exchange); .doOnError(bindingResultMono::onError) .doOnSuccess(aVoid -> { validateIfApplicable(binder, parameter); BindingResult errors = binder.getBindingResult(); model.put(BindingResult.MODEL_KEY_PREFIX + name, errors); if (errors.hasErrors() && !hasErrorsArgument(parameter)) { throw new WebExchangeBindException(parameter, errors);
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve()); Class<?> valueType = (adapter != null ? type.resolveGeneric(0) : parameter.getParameterType()); String name = getAttributeName(valueType, parameter); Mono<?> valueMono = getAttributeMono(name, valueType, parameter, context, exchange); .doOnError(bindingResultMono::onError) .doOnSuccess(aVoid -> { validateIfApplicable(binder, parameter); BindingResult errors = binder.getBindingResult(); model.put(BindingResult.MODEL_KEY_PREFIX + name, errors); if (errors.hasErrors() && checkErrorsArgument(parameter)) { throw new WebExchangeBindException(parameter, errors);
@Test public void supports() throws Exception { ModelAttributeMethodArgumentResolver resolver = new ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry.getSharedInstance(), false); MethodParameter param = this.testMethod.annotPresent(ModelAttribute.class).arg(Foo.class); assertTrue(resolver.supportsParameter(param)); param = this.testMethod.annotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); assertTrue(resolver.supportsParameter(param)); param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); assertFalse(resolver.supportsParameter(param)); param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); assertFalse(resolver.supportsParameter(param)); }
private Mono<?> getAttributeMono(String attributeName, Class<?> attributeType, MethodParameter param, BindingContext context, ServerWebExchange exchange) { Object attribute = context.getModel().asMap().get(attributeName); if (attribute == null) { attribute = createAttribute(attributeName, attributeType, param, context, exchange); } if (attribute != null) { ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute); if (adapterFrom != null) { Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding supports single-value async types."); return Mono.from(adapterFrom.toPublisher(attribute)); } } return Mono.justOrEmpty(attribute); }
@Nullable private Object findAndRemoveReactiveAttribute(Model model, String attributeName) { return model.asMap().entrySet().stream() .filter(entry -> { if (!entry.getKey().startsWith(attributeName)) { return false; } ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, entry.getValue()); if (adapter == null) { return false; } String name = attributeName + ClassUtils.getShortName(adapter.getReactiveType()); return entry.getKey().equals(name); }) .findFirst() .map(entry -> { // Remove since we will be re-inserting the resolved attribute value model.asMap().remove(entry.getKey()); return entry.getValue(); }) .orElse(null); }
private Mono<?> createAttribute( String attributeName, Class<?> clazz, BindingContext context, ServerWebExchange exchange) { Constructor<?> ctor = BeanUtils.findPrimaryConstructor(clazz); if (ctor == null) { Constructor<?>[] ctors = clazz.getConstructors(); if (ctors.length == 1) { ctor = ctors[0]; } else { try { ctor = clazz.getDeclaredConstructor(); } catch (NoSuchMethodException ex) { throw new IllegalStateException("No primary or default constructor found for " + clazz, ex); } } } return constructAttribute(ctor, attributeName, context, exchange); }
@Test public void supportsWithDefaultResolution() throws Exception { ModelAttributeMethodArgumentResolver resolver = new ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry.getSharedInstance(), true); MethodParameter param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); assertTrue(resolver.supportsParameter(param)); param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); assertTrue(resolver.supportsParameter(param)); param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(String.class); assertFalse(resolver.supportsParameter(param)); param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, String.class); assertFalse(resolver.supportsParameter(param)); }
@Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { return true; } if (this.useDefaultResolution) { Class<?> clazz = parameter.getParameterType(); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(clazz); if (adapter != null) { if (adapter.isNoValue() || adapter.isMultiValue()) { return false; } clazz = ResolvableType.forMethodParameter(parameter).getGeneric(0).getRawClass(); } return !BeanUtils.isSimpleProperty(clazz); } return false; }
result.add(new ModelAttributeMethodArgumentResolver(reactiveRegistry, false)); result.add(new ModelAttributeMethodArgumentResolver(reactiveRegistry, true));
resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), getReactiveAdapterRegistry())); resolvers.add(new ModelAttributeMethodArgumentResolver(getReactiveAdapterRegistry())); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ModelAttributeMethodArgumentResolver(getReactiveAdapterRegistry(), true)); return resolvers;