@EnableWebFlux setup supports WebSocketHandler
Closes gh-22587
This commit is contained in:
parent
8f369ffed5
commit
591ab8a00a
|
@ -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
|
|||
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);
|
||||
|
|
|
@ -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 {
|
|||
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,
|
||||
|
|
|
@ -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;
|
|||
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 {
|
|||
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
|
||||
|
|
|
@ -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;
|
|||
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 {
|
|||
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));
|
||||
|
|
|
@ -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;
|
|||
|
||||
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;
|
|||
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 {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
|
|
@ -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 {
|
|||
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 {
|
|||
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));
|
||||
|
|
|
@ -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
|
|||
|
||||
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
|
|||
|
||||
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`.
|
|||
=== 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
|
||||
|
|
|
@ -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…
Reference in New Issue