diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java b/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java index 482e007bd2..13e8d3374f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,18 +23,23 @@ import org.springframework.web.bind.support.WebExchangeDataBinder; import org.springframework.web.server.ServerWebExchange; /** - * A context for binding requests to method arguments that provides access to - * the default model, data binding, validation, and type conversion. + * Context to assist with processing a request and binding it onto Objects. + * + *

Provides methods to create a {@link WebExchangeDataBinder} for a specific + * target, command Object to apply data binding and validation to, or without a + * target Object for simple type conversion from request values. + * + *

Container for the default model for the request. * * @author Rossen Stoyanchev * @since 5.0 */ public class BindingContext { - private final Model model = new BindingAwareConcurrentModel(); - private final WebBindingInitializer initializer; + private final Model model = new BindingAwareConcurrentModel(); + public BindingContext() { this(null); @@ -54,39 +59,23 @@ public class BindingContext { /** - * Create a {@link WebExchangeDataBinder} for applying data binding, type - * conversion, and validation on the given "target" object. + * Create a {@link WebExchangeDataBinder} to apply data binding and + * validation with on the target, command object. + * * @param exchange the current exchange * @param target the object to create a data binder for * @param name the name of the target object - * @return the {@link WebExchangeDataBinder} instance + * + * @return the created data binder */ public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, Object target, String name) { - WebExchangeDataBinder dataBinder = createBinderInstance(target, name); + WebExchangeDataBinder dataBinder = new WebExchangeDataBinder(target, name); if (this.initializer != null) { this.initializer.initBinder(dataBinder); } return initDataBinder(dataBinder, exchange); } - /** - * Create a {@link WebExchangeDataBinder} without a "target" object, i.e. - * for applying type conversion to simple types. - * @param exchange the current exchange - * @param name the name of the target object - * @return a Mono for the created {@link WebExchangeDataBinder} instance - */ - public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, String name) { - return createDataBinder(exchange, null, name); - } - - /** - * Create the data binder instance. - */ - protected WebExchangeDataBinder createBinderInstance(Object target, String objectName) { - return new WebExchangeDataBinder(target, objectName); - } - /** * Initialize the data binder instance for the given exchange. */ @@ -94,4 +83,17 @@ public class BindingContext { return binder; } + /** + * Create a {@link WebExchangeDataBinder} without a target object for type + * conversion of request values to simple types. + * + * @param exchange the current exchange + * @param name the name of the target object + * + * @return the created data binder + */ + public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, String name) { + return createDataBinder(exchange, null, name); + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java index c87c69def0..aae9d30e98 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,30 +19,28 @@ package org.springframework.web.reactive.result.method.annotation; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Optional; -import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.bind.support.WebExchangeDataBinder; -import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.BindingContext; -import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod; import org.springframework.web.server.ServerWebExchange; /** - * An extension of {@link BindingContext} that uses {@code @InitBinder} methods - * to initialize a data binder instance. + * Variant of {@link BindingContext} that further initializes {@code DataBinder} + * instances through {@code @InitBinder} methods. * * @author Rossen Stoyanchev * @since 5.0 */ -public class InitBinderBindingContext extends BindingContext { +class InitBinderBindingContext extends BindingContext { private final List binderMethods; - /** BindingContext for @InitBinder method invocation */ - private final BindingContext bindingContext; + /* Simple BindingContext to help with the invoking @InitBinder methods */ + private final BindingContext binderMethodContext; public InitBinderBindingContext(WebBindingInitializer initializer, @@ -50,38 +48,41 @@ public class InitBinderBindingContext extends BindingContext { super(initializer); this.binderMethods = binderMethods; - this.bindingContext = new BindingContext(initializer); + this.binderMethodContext = new BindingContext(initializer); } @Override - protected WebExchangeDataBinder initDataBinder(WebExchangeDataBinder binder, - ServerWebExchange exchange) { + protected WebExchangeDataBinder initDataBinder(WebExchangeDataBinder binder, ServerWebExchange exchange) { this.binderMethods.stream() - .filter(method -> isBinderMethodApplicable(method, binder)) - .forEach(method -> invokeInitBinderMethod(binder, exchange, method)); + .filter(binderMethod -> { + InitBinder annotation = binderMethod.getMethodAnnotation(InitBinder.class); + Collection names = Arrays.asList(annotation.value()); + return (names.size() == 0 || names.contains(binder.getObjectName())); + }) + .forEach(method -> invokeBinderMethod(binder, exchange, method)); return binder; } - /** - * Whether the given {@code @InitBinder} method should be used to initialize - * the given WebDataBinder instance. By default we check the attributes - * names of the annotation, if present. - */ - protected boolean isBinderMethodApplicable(HandlerMethod binderMethod, WebDataBinder binder) { - InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class); - Collection names = Arrays.asList(annot.value()); - return (names.size() == 0 || names.contains(binder.getObjectName())); - } + private void invokeBinderMethod(WebExchangeDataBinder binder, ServerWebExchange exchange, + SyncInvocableHandlerMethod binderMethod) { - private void invokeInitBinderMethod(WebExchangeDataBinder binder, ServerWebExchange exchange, - SyncInvocableHandlerMethod method) { + Optional returnValue = binderMethod + .invokeForHandlerResult(exchange, this.binderMethodContext, binder) + .getReturnValue(); - HandlerResult result = method.invokeForHandlerResult(exchange, this.bindingContext, binder); - if (result.getReturnValue().isPresent()) { - throw new IllegalStateException("@InitBinder methods should return void: " + method); + if (returnValue.isPresent()) { + throw new IllegalStateException( + "@InitBinder methods should return void: " + binderMethod); + } + + // Should not happen (no argument resolvers)... + + if (!this.binderMethodContext.getModel().asMap().isEmpty()) { + throw new IllegalStateException( + "@InitBinder methods should not add model attributes: " + binderMethod); } }