Browse Source

@EnableWebFlux setup supports WebSocketHandler

Closes gh-22587
pull/25684/head
Rossen Stoyanchev 5 years ago
parent
commit
591ab8a00a
  1. 7
      spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java
  2. 20
      spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java
  3. 15
      spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java
  4. 9
      spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java
  5. 45
      spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/WebSocketHandlerAdapter.java
  6. 15
      spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java
  7. 43
      src/docs/asciidoc/web/webflux-websocket.adoc
  8. 47
      src/docs/asciidoc/web/webflux.adoc

7
spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java

@ -27,6 +27,7 @@ import org.springframework.validation.MessageCodesResolver; @@ -27,6 +27,7 @@ import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
import org.springframework.web.reactive.socket.server.WebSocketService;
/**
* A subclass of {@code WebFluxConfigurationSupport} that detects and delegates
@ -98,6 +99,12 @@ public class DelegatingWebFluxConfiguration extends WebFluxConfigurationSupport @@ -98,6 +99,12 @@ public class DelegatingWebFluxConfiguration extends WebFluxConfigurationSupport
return (messageCodesResolver != null ? messageCodesResolver : super.getMessageCodesResolver());
}
@Override
protected WebSocketService getWebSocketService() {
WebSocketService service = this.configurers.getWebSocketService();
return (service != null ? service : super.getWebSocketService());
}
@Override
protected void configureViewResolvers(ViewResolverRegistry registry) {
this.configurers.configureViewResolvers(registry);

20
spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java

@ -65,6 +65,8 @@ import org.springframework.web.reactive.result.method.annotation.ResponseBodyRes @@ -65,6 +65,8 @@ import org.springframework.web.reactive.result.method.annotation.ResponseBodyRes
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler;
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.reactive.socket.server.WebSocketService;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver;
@ -431,6 +433,24 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { @@ -431,6 +433,24 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
return new SimpleHandlerAdapter();
}
@Bean
public WebSocketHandlerAdapter webFluxWebSocketHandlerAdapter() {
WebSocketService service = getWebSocketService();
WebSocketHandlerAdapter adapter = (service != null ?
new WebSocketHandlerAdapter(service) : new WebSocketHandlerAdapter());
// For backwards compatibility, lower the (default) priority
int defaultOrder = adapter.getOrder();
adapter.setOrder(defaultOrder + 1);
return adapter;
}
@Nullable
protected WebSocketService getWebSocketService() {
return null;
}
@Bean
public ResponseEntityResultHandler responseEntityResultHandler(
@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,

15
spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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.
@ -25,6 +25,7 @@ import org.springframework.validation.MessageCodesResolver; @@ -25,6 +25,7 @@ import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
import org.springframework.web.reactive.socket.server.WebSocketService;
/**
* Defines callback methods to customize the configuration for WebFlux
@ -124,6 +125,18 @@ public interface WebFluxConfigurer { @@ -124,6 +125,18 @@ public interface WebFluxConfigurer {
return null;
}
/**
* Provide the {@link WebSocketService} to create
* {@link org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter}
* with. This can be used to configure server-specific properties through the
* {@link org.springframework.web.reactive.socket.server.RequestUpgradeStrategy}.
* @since 5.3
*/
@Nullable
default WebSocketService getWebSocketService() {
return null;
}
/**
* Configure view resolution for rendering responses with a view and a model,
* where the view is typically an HTML template but could also be based on

9
spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 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.
@ -30,6 +30,7 @@ import org.springframework.validation.MessageCodesResolver; @@ -30,6 +30,7 @@ import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
import org.springframework.web.reactive.socket.server.WebSocketService;
/**
* A {@link WebFluxConfigurer} that delegates to one or more others.
@ -70,6 +71,12 @@ public class WebFluxConfigurerComposite implements WebFluxConfigurer { @@ -70,6 +71,12 @@ public class WebFluxConfigurerComposite implements WebFluxConfigurer {
this.delegates.forEach(delegate -> delegate.addResourceHandlers(registry));
}
@Nullable
@Override
public WebSocketService getWebSocketService() {
return createSingleBean(WebFluxConfigurer::getWebSocketService, WebSocketService.class);
}
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
this.delegates.forEach(delegate -> delegate.configureArgumentResolvers(configurer));

45
spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/WebSocketHandlerAdapter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 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.
@ -17,8 +17,8 @@ package org.springframework.web.reactive.socket.server.support; @@ -17,8 +17,8 @@ package org.springframework.web.reactive.socket.server.support;
import reactor.core.publisher.Mono;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.HandlerAdapter;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.socket.WebSocketHandler;
@ -26,18 +26,30 @@ import org.springframework.web.reactive.socket.server.WebSocketService; @@ -26,18 +26,30 @@ import org.springframework.web.reactive.socket.server.WebSocketService;
import org.springframework.web.server.ServerWebExchange;
/**
* {@link HandlerAdapter} that allows using a {@link WebSocketHandler} with the
* generic {@link DispatcherHandler} mapping URLs directly to such handlers.
* Requests are handled by delegating to the configured {@link WebSocketService}
* which by default is {@link HandshakeWebSocketService}.
* {@code HandlerAdapter} that allows
* {@link org.springframework.web.reactive.DispatcherHandler} to support
* handlers of type {@link WebSocketHandler} with such handlers mapped to
* URL patterns via
* {@link org.springframework.web.reactive.handler.SimpleUrlHandlerMapping}.
*
* <p>Requests are handled by delegating to a
* {@link WebSocketService}, by default {@link HandshakeWebSocketService},
* which checks the WebSocket handshake request parameters, upgrades to a
* WebSocket interaction, and uses the {@link WebSocketHandler} to handle it.
*
* <p>As of 5.3 the WebFlux Java configuration, imported via
* {@code @EnableWebFlux}, includes a declaration of this adapter and therefore
* it no longer needs to be present in application configuration.
*
* @author Rossen Stoyanchev
* @since 5.0
*/
public class WebSocketHandlerAdapter implements HandlerAdapter {
public class WebSocketHandlerAdapter implements HandlerAdapter, Ordered {
private final WebSocketService webSocketService;
private int order = 2;
/**
* Default constructor that creates and uses a
@ -56,6 +68,25 @@ public class WebSocketHandlerAdapter implements HandlerAdapter { @@ -56,6 +68,25 @@ public class WebSocketHandlerAdapter implements HandlerAdapter {
}
/**
* Set the order value for this adapter.
* <p>By default this is set to 2.
* @param order the value to set to
* @since 5.3
*/
public void setOrder(int order) {
this.order = order;
}
/**
* Return the {@link #setOrder(int) configured} order for this instance.
* @since 5.3
*/
@Override
public int getOrder() {
return this.order;
}
/**
* Return the configured {@code WebSocketService} to handle requests.
*/

15
spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java

@ -37,11 +37,14 @@ import org.springframework.validation.Validator; @@ -37,11 +37,14 @@ import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.reactive.socket.server.WebSocketService;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
@ -73,6 +76,7 @@ public class DelegatingWebFluxConfigurationTests { @@ -73,6 +76,7 @@ public class DelegatingWebFluxConfigurationTests {
delegatingConfig.setApplicationContext(new StaticApplicationContext());
given(webFluxConfigurer.getValidator()).willReturn(null);
given(webFluxConfigurer.getMessageCodesResolver()).willReturn(null);
given(webFluxConfigurer.getWebSocketService()).willReturn(null);
}
@ -125,6 +129,17 @@ public class DelegatingWebFluxConfigurationTests { @@ -125,6 +129,17 @@ public class DelegatingWebFluxConfigurationTests {
verify(webFluxConfigurer).configurePathMatching(any(PathMatchConfigurer.class));
}
@Test
void webSocketService() {
WebSocketService service = mock(WebSocketService.class);
given(webFluxConfigurer.getWebSocketService()).willReturn(service);
delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer));
WebSocketHandlerAdapter adapter = delegatingConfig.webFluxWebSocketHandlerAdapter();
assertThat(adapter.getWebSocketService()).isSameAs(service);
}
@Test
public void responseBodyResultHandler() {
delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer));

43
src/docs/asciidoc/web/webflux-websocket.adoc

@ -54,7 +54,7 @@ The following example shows how to do so: @@ -54,7 +54,7 @@ The following example shows how to do so:
}
----
Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the following example shows:
Then you can map it to a URL:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
@ -70,11 +70,6 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi @@ -70,11 +70,6 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi
return new SimpleUrlHandlerMapping(map, order);
}
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -90,6 +85,34 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi @@ -90,6 +85,34 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi
return SimpleUrlHandlerMapping(map, order)
}
}
----
If using the <<web-reactive.adoc#webflux-config, WebFlux Config>> there is nothing
further to do, or otherwise if not using the WebFlux config you'll need to declare a
`WebSocketHandlerAdapter` as shown below:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
class WebConfig {
// ...
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class WebConfig {
// ...
@Bean
fun handlerAdapter() = WebSocketHandlerAdapter()
@ -333,9 +356,11 @@ into the attributes of the `WebSocketSession`. @@ -333,9 +356,11 @@ into the attributes of the `WebSocketSession`.
=== Server Configation
[.small]#<<web.adoc#websocket-server-runtime-configuration, Same as in the Servlet stack>>#
The `RequestUpgradeStrategy` for each server exposes WebSocket-related configuration
options available for the underlying WebSocket engine. The following example sets
WebSocket options when running on Tomcat:
The `RequestUpgradeStrategy` for each server exposes configuration specific to the
underlying WebSocket server engine. When using the WebFlux Java config you can customize
such properties as shown in the corresponding section of the
<<web-reactive.adoc#webflux-config-websocket-service, WebFlux Config>>, or otherwise if
not using the WebFlux config, use the below:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java

47
src/docs/asciidoc/web/webflux.adoc

@ -4289,6 +4289,53 @@ reliance on it. @@ -4289,6 +4289,53 @@ reliance on it.
[[webflux-config-websocket-service]]
=== WebSocketService
The WebFlux Java config declares of a `WebSocketHandlerAdapter` bean which provides
support for the invocation of WebSocket handlers. That means all that remains to do in
order to handle a WebSocket handshake request is to map a `WebSocketHandler` to a URL
via `SimpleUrlHandlerMapping`.
In some cases it may be necessary to create the `WebSocketHandlerAdapter` bean with a
provided `WebSocketService` service which allows configuring WebSocket server properties.
For example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public WebSocketService getWebSocketService() {
TomcatRequestUpgradeStrategy strategy = new TomcatRequestUpgradeStrategy();
strategy.setMaxSessionIdleTimeout(0L);
return new HandshakeWebSocketService(strategy);
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
fun webSocketService(): WebSocketService {
val strategy = TomcatRequestUpgradeStrategy().apply {
setMaxSessionIdleTimeout(0L)
}
return HandshakeWebSocketService(strategy)
}
}
----
[[webflux-config-advanced-java]]
=== Advanced Configuration Mode
[.small]#<<web.adoc#mvc-config-advanced-java, Web MVC>>#

Loading…
Cancel
Save