简体   繁体   English

Spring WebFlux 和 WebSocket

[英]Spring WebFlux and WebSocket

I am trying to add WebSocket functionality to an existing application which uses Spring WebFlux.我正在尝试将 WebSocket 功能添加到使用 Spring WebFlux 的现有应用程序中。 It uses:它用:

  • Spring boot 2.2.1.RELEASE Spring 启动 2.2.1.RELEASE
  • Tomcat container Tomcat 集装箱
  • Configured to serve jsp pages配置为服务 jsp 页面

When I try to connect to it through JavaScript (from inside a jsp page) I am receiving the error " failed: Error during WebSocket handshake: Unexpected response code: 404 "当我尝试通过 JavaScript(从 jsp 页面内部)连接到它时,我收到错误“失败:WebSocket 握手期间出错:意外响应代码:404

I noted that if I create a controller class mapping url '/ws/event-emitter' (the one mapped in my websocket configuration) accepting GET requests, it receives the requests from Javascript call when it tries to open the websocket connection, which made me more confused. I noted that if I create a controller class mapping url '/ws/event-emitter' (the one mapped in my websocket configuration) accepting GET requests, it receives the requests from Javascript call when it tries to open the websocket connection, which made我更糊涂了。

What am I missing?我错过了什么?

After read some tutorials on the Internet I set my configuration as below:在互联网上阅读了一些教程后,我将配置设置如下:

<?xml version="1.0" encoding="UTF-8"?>

https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE br.com.samsung monitor 0.0.1-SNAPSHOT monitor Health Check Monitor https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE br.com.samsung monitor 0.0.1 -SNAPSHOT monitor 健康检查监视器

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
     -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.projectreactor</groupId>
        <artifactId>reactor-spring</artifactId>
        <version>1.0.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.microsoft.sqlserver</groupId>
        <artifactId>mssql-jdbc</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- MongoDB -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-releasetrain</artifactId>
        <version>Lovelace-SR9</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

@Configuration

public class ReactiveWebSocketConfiguration {公共 class ReactiveWebSocketConfiguration {

@Autowired
private WebSocketHandler webSocketHandler;

@Bean
public HandlerMapping webSocketHandlerMapping() {
    Map<String, WebSocketHandler> map = new HashMap<>();
    map.put("/ws/event-emitter", webSocketHandler);

    SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
    handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
    handlerMapping.setUrlMap(map);
    return handlerMapping;
}

@Bean
public WebSocketHandlerAdapter handlerAdapter(WebSocketService webSocketService) {
    return new WebSocketHandlerAdapter(webSocketService);
}

@Bean
public WebSocketService webSocketService() {
    TomcatRequestUpgradeStrategy strategy = new TomcatRequestUpgradeStrategy();
    strategy.setMaxSessionIdleTimeout(0L);
    return new HandshakeWebSocketService(strategy);
}

} }

@Component public class ReactiveWebSocketHandler implements WebSocketHandler { @Component public class ReactiveWebSocketHandler 实现 WebSocketHandler {

private static final ObjectMapper json = new ObjectMapper();

private Flux<String> eventFlux = Flux.generate(sink -> {
    Event event = new Event(randomUUID().toString(), now().toString());
    try {
        sink.next(json.writeValueAsString(event));
    } catch (JsonProcessingException e) {
        sink.error(e);
    }
});

private Flux<String> intervalFlux = Flux.interval(Duration.ofMillis(1000L))
  .zipWith(eventFlux, (time, event) -> event);

@Override
public Mono<Void> handle(WebSocketSession webSocketSession) {
    return webSocketSession.send(intervalFlux
      .map(webSocketSession::textMessage))
      .and(webSocketSession.receive()
        .map(WebSocketMessage::getPayloadAsText).log());
}

} }

 <script> var clientWebSocket = new WebSocket("ws://localhost:6600/ws/event-emitter"); clientWebSocket.onopen = function() { console.log("clientWebSocket.onopen", clientWebSocket); console.log("clientWebSocket.readyState", "websocketstatus"); clientWebSocket.send("event-me-from-browser"); } clientWebSocket.onclose = function(error) { console.log("clientWebSocket.onclose", clientWebSocket, error); events("Closing connection"); } clientWebSocket.onerror = function(error) { console.log("clientWebSocket.onerror", clientWebSocket, error); events("An error occured"); } clientWebSocket.onmessage = function(error) { console.log("clientWebSocket.onmessage", clientWebSocket, error); events(error.data); } function events(responseEvent) { document.querySelector(".events").innerHTML += responseEvent + "<br>"; } </script>

I could make it work removing spring-boot-starter-web and tomcat-embed-jasper dependencies.我可以使它工作删除spring-boot-starter-webtomcat-embed-jasper依赖项。 I suppose spring-boot-starter-web might conflict with spring-boot-starter-webflux .我想spring-boot-starter-web可能与spring-boot-starter-webflux 冲突

Also I have changed my ReactiveWebSocketConfiguration class to set WebSocketService bean using ReactorNettyRequestUpgradeStrategy instead of TomcatRequestUpgradeStrategy :我还更改了我的ReactiveWebSocketConfiguration class 以使用ReactorNettyRequestUpgradeStrategy而不是TomcatRequestUpgradeStrategy设置WebSocketService bean:

@Bean
public WebSocketService webSocketService() {
    return new HandshakeWebSocketService(new ReactorNettyRequestUpgradeStrategy());
}

After those changes my jsp pages stopped working as my web container changed from Tomcat to Netty Web Server.在这些更改之后,我的 jsp 页面停止工作,因为我的 web 容器从 Tomcat 更改为 Netty ZC6E190B2504633CCE8E. I had to use Thymeleaf (added spring-boot-starter-thymeleaf dependency, configured its basic strucute and converted my pages to.html files) instead of traditional jsp.我不得不使用 Thymeleaf(添加了spring-boot-starter-thymeleaf依赖项,配置了它的基本结构并将我的页面转换为 .html 文件)而不是传统的 ZEC407CCE6B649DAA8E320157E576。

I have opted to change from traditional jsp to Thymeleaf due to time constraints as my application has few and simple pages.由于时间限制,我选择从传统的 jsp 更改为 Thymeleaf,因为我的应用程序页面很少且简单。

I have created a very simple project with all those configuration including a dummy page connecting to the websocket via javascript.我创建了一个非常简单的项目,其中包含所有这些配置,包括一个通过 javascript 连接到 websocket 的虚拟页面。

https://github.com/atilio-araujo/spring-webflux-websocket https://github.com/atilio-araujo/spring-webflux-websocket

Spring WebFlux WebSocket: Spring WebFlux WebSocket:

Just adding this answer for people who are trying to achieve websocket with webflux .只需为尝试使用webflux实现websocket的人添加此答案。

  1. The OP has both web and webflux dependencies for some reason.由于某种原因,OP 同时具有webwebflux依赖项。 However you just need WebFlux dependency only.但是,您只需要WebFlux依赖项。
  2. Implement a WebSocketHandler实现一个 WebSocketHandler
    @Service
    public class WebFluxWebSocketHandler implements WebSocketHandler {
    
        @Override
        public Mono<Void> handle(WebSocketSession webSocketSession) {
            Flux<WebSocketMessage> stringFlux = webSocketSession.receive()
                    .map(WebSocketMessage::getPayloadAsText)
                    .map(String::toUpperCase)
                    .map(webSocketSession::textMessage);
            return webSocketSession.send(stringFlux);
        }
    
    }
  1. Add a path - handler mapping.添加路径 - 处理程序映射。 Here the handler is an instance of the websockethandler.这里的处理程序是 websockethandler 的一个实例。
     @Bean
       public HandlerMapping handlerMapping(){
           Map<String, WebFluxWebSocketHandler> handlerMap = Map.of(
                   "/test", handler
           );
           return new SimpleUrlHandlerMapping(handlerMap, 1);
       }

That's it!而已! Now the client should be able to connect to ws://localhost:8080/test现在客户端应该能够连接到ws://localhost:8080/test

Check here for more info - https://www.vinsguru.com/spring-webflux-websocket/在这里查看更多信息 - https://www.vinsguru.com/spring-webflux-websocket/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM