Expose ServerCodecConfigurer as a bean

With this commit, ServerCodecConfigurer is now exposed as a bean in
order to be provided to DefaultServerWebExchange via
WebHttpHandlerBuilder and HttpWebHandlerAdapter. This allows
DefaultServerWebExchange to get configured codecs for reading form or
multipart requests.

Issue: SPR-14546
This commit is contained in:
Sebastien Deleuze
2017-04-28 11:36:50 +02:00
parent a712c19661
commit 8e272bc5b0
9 changed files with 93 additions and 29 deletions

View File

@@ -34,7 +34,8 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.http.codec.FormHttpMessageReader;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
@@ -46,6 +47,8 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import org.springframework.web.server.session.WebSessionManager;
import static org.springframework.http.MediaType.*;
/**
* Default implementation of {@link ServerWebExchange}.
*
@@ -56,8 +59,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
private static final List<HttpMethod> SAFE_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD);
private static final FormHttpMessageReader FORM_READER = new FormHttpMessageReader();
private static final ResolvableType FORM_DATA_VALUE_TYPE =
ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class);
@@ -85,26 +86,35 @@ public class DefaultServerWebExchange implements ServerWebExchange {
* Alternate constructor with a WebSessionManager parameter.
*/
public DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
WebSessionManager sessionManager) {
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer) {
Assert.notNull(request, "'request' is required");
Assert.notNull(response, "'response' is required");
Assert.notNull(response, "'sessionManager' is required");
Assert.notNull(response, "'formReader' is required");
Assert.notNull(sessionManager, "'sessionManager' is required");
Assert.notNull(codecConfigurer, "'codecConfigurer' is required");
this.request = request;
this.response = response;
this.sessionMono = sessionManager.getSession(this).cache();
this.formDataMono = initFormData(request);
this.formDataMono = initFormData(request, codecConfigurer);
this.requestParamsMono = initRequestParams(request, this.formDataMono);
}
private static Mono<MultiValueMap<String, String>> initFormData(ServerHttpRequest request) {
@SuppressWarnings("unchecked")
private static Mono<MultiValueMap<String, String>> initFormData(
ServerHttpRequest request, ServerCodecConfigurer codecConfigurer) {
MediaType contentType;
try {
contentType = request.getHeaders().getContentType();
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
return FORM_READER
if (APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
return ((HttpMessageReader<MultiValueMap<String, String>>)codecConfigurer
.getReaders()
.stream()
.filter(messageReader -> messageReader.canRead(FORM_DATA_VALUE_TYPE, APPLICATION_FORM_URLENCODED))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find HttpMessageReader that supports " + APPLICATION_FORM_URLENCODED)))
.readMono(FORM_DATA_VALUE_TYPE, request, Collections.emptyMap())
.switchIfEmpty(EMPTY_FORM_DATA)
.cache();

View File

@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
@@ -38,6 +39,7 @@ import org.springframework.web.server.session.WebSessionManager;
* then invokes the target {@code WebHandler}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 5.0
*/
public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler {
@@ -46,6 +48,8 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
private WebSessionManager sessionManager = new DefaultWebSessionManager();
private ServerCodecConfigurer codecConfigurer;
public HttpWebHandlerAdapter(WebHandler delegate) {
super(delegate);
@@ -71,6 +75,24 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return this.sessionManager;
}
/**
* Configure a custom {@link ServerCodecConfigurer}. The provided instance is set on
* each created {@link DefaultServerWebExchange}.
* <p>By default this is set to {@link ServerCodecConfigurer#create()}.
* @param codecConfigurer the codec configurer to use
*/
public void setCodecConfigurer(ServerCodecConfigurer codecConfigurer) {
Assert.notNull(codecConfigurer, "ServerCodecConfigurer must not be null");
this.codecConfigurer = codecConfigurer;
}
/**
* Return the configured {@link ServerCodecConfigurer}.
*/
public ServerCodecConfigurer getCodecConfigurer() {
return this.codecConfigurer != null ? this.codecConfigurer : ServerCodecConfigurer.create();
}
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
@@ -87,7 +109,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
}
protected ServerWebExchange createExchange(ServerHttpRequest request, ServerHttpResponse response) {
return new DefaultServerWebExchange(request, response, this.sessionManager);
return new DefaultServerWebExchange(request, response, this.sessionManager, getCodecConfigurer());
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -51,6 +52,7 @@ import org.springframework.web.server.session.WebSessionManager;
* {@link #applicationContext(ApplicationContext)}, or a mix of both.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 5.0
* @see HttpWebHandlerAdapter
*/
@@ -62,6 +64,9 @@ public class WebHttpHandlerBuilder {
/** Well-known name for the WebSessionManager in the bean factory. */
public static final String WEB_SESSION_MANAGER_BEAN_NAME = "webSessionManager";
/** Well-known name for the ServerCodecConfigurer in the bean factory. */
public static final String SERVER_CODEC_CONFIGURER_BEAN_NAME = "serverCodecConfigurer";
private final WebHandler webHandler;
@@ -71,6 +76,8 @@ public class WebHttpHandlerBuilder {
private WebSessionManager sessionManager;
private ServerCodecConfigurer codecConfigurer;
/**
* Private constructor.
@@ -102,6 +109,8 @@ public class WebHttpHandlerBuilder {
* ordered.
* <li>{@link WebSessionManager} [0..1] -- looked up by the name
* {@link #WEB_SESSION_MANAGER_BEAN_NAME}.
* <li>{@link ServerCodecConfigurer} [0..1] -- looked up by the name
* {@link #SERVER_CODEC_CONFIGURER_BEAN_NAME}.
* </ul>
* @param context the application context to use for the lookup
* @return the prepared builder
@@ -126,6 +135,14 @@ public class WebHttpHandlerBuilder {
// Fall back on default
}
try {
builder.codecConfigurer(
context.getBean(SERVER_CODEC_CONFIGURER_BEAN_NAME, ServerCodecConfigurer.class));
}
catch (NoSuchBeanDefinitionException ex) {
// Fall back on default
}
return builder;
}
@@ -184,6 +201,16 @@ public class WebHttpHandlerBuilder {
return this;
}
/**
* Configure the {@link ServerCodecConfigurer} to set on the
* {@link ServerWebExchange WebServerExchange}.
* @param codecConfigurer the codec configurer
*/
public WebHttpHandlerBuilder codecConfigurer(ServerCodecConfigurer codecConfigurer) {
this.codecConfigurer = codecConfigurer;
return this;
}
/**
* Build the {@link HttpHandler}.
@@ -199,6 +226,9 @@ public class WebHttpHandlerBuilder {
if (this.sessionManager != null) {
adapted.setSessionManager(this.sessionManager);
}
if (this.codecConfigurer != null) {
adapted.setCodecConfigurer(this.codecConfigurer);
}
return adapted;
}