JavaScript 基于 WebSocket 客户端使用 Spring Reactor Web 套接字未接收到浏览器控制台的任何输出

JavaScript based WebSocket Client Not Receiving any Output to Browser Console using Spring Reactor Web Sockets

我正在学习使用 Pivotal 的 Reactor Framework 设置网络套接字的教程 API。

该示例需要在 运行 Spring 引导反应微服务之后启动 Google Chrome 网络浏览器并打开特定的 URL(带端口),然后单击“查看/源”,然后单击“控制台”。

这应该会向 Google Chrome 的控制台标准输出生成一条日志消息。

然而,没有任何输出...


项目结构:

rswebsockets
│
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── reactive
    │   │           ├── RsWebSocketsApplication.java
    │   │           ├── config
    │   │           │   └── WebSocketConfig.java
    │   │           ├── model
    │   │           │   ├── GreetingRequest.java
    │   │           │   └── GreetingResponse.java
    │   │           └── service
    │   │               └── GreetingService.java
    │   └── resources
    │       ├── application.properties
    │       └── static
    │           └── ws.html
    └── test
        └── java
            └── com
                └── reactive
                    └── RsWebSocketsApplicationTests.java

build.gradle:

plugins {
    id 'org.springframework.boot' version '2.2.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.reactive'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '14'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'io.projectreactor:reactor-test'
}

test {
    useJUnitPlatform()
}

代码库:

rswebsockets/src/main/java/com/reactive/RsWebSocketsApplication.java:

package com.reactive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RsWebSocketsApplication {

    public static void main(String[] args) {
        SpringApplication.run(RsWebSocketsApplication.class, args);
    }

}

rswebsockets/src/main/java/com/reactive/config/WebSocketConfig.java:

package com.reactive.config;

import com.reactive.model.GreetingRequest;
import com.reactive.model.GreetingResponse;
import com.reactive.service.GreetingService;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;

import java.util.Map;

public class WebSocketConfig {

    @Bean
    SimpleUrlHandlerMapping simpleUrlHandlerMapping(WebSocketHandler wsh) {
        return new SimpleUrlHandlerMapping(Map.of("/ws/greetings", wsh), 10);
    }

    @Bean
    WebSocketHandler webSocketHandler(GreetingService greetingService) {
        return session -> {
           var receive = session
                            .receive()
                            .map(WebSocketMessage::getPayloadAsText)
                            .map(GreetingRequest::new)
                            .flatMap(greetingService::greet)
                            .map(GreetingResponse::getMessage)
                            .map(session::textMessage);
           return session.send(receive);
        };
    }

    @Bean
    WebSocketHandlerAdapter webSockerHandlerAdapter() {
        return new WebSocketHandlerAdapter();
    }
}

rswebsockets/src/main/java/com/reactive/model/GreetingRequest.java:

package com.reactive.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GreetingRequest {

    String name;

}

rswebsockets/src/main/java/com/reactive/model/GreetingResponse.java:

package com.reactive.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GreetingResponse {

    String message;

}

rswebsockets/src/main/java/com/reactive/service/GreetingService.java:

package com.reactive.service;

import com.reactive.model.GreetingRequest;
import com.reactive.model.GreetingResponse;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.time.Duration;
import java.time.Instant;
import java.util.stream.Stream;

@Service
public class GreetingService {

    public Flux<GreetingResponse> greet(GreetingRequest request) {
        return Flux.fromStream(
                Stream.generate(() -> new GreetingResponse("Hello " + request.getName() + " @ " + Instant.now())))
                      .delayElements(Duration.ofSeconds(1));

    }
}

rswebsockets/src/main/resources/static/ws.html:

<html>
<body>
<script>
    window.addEventListener('load', function (e) {
        var ws = new WebSocket('ws://localhost:8080/ws/greetings')
        ws.addEventListener('open', function (e) {
            ws.send('Livelessons Fans')
        });
        ws.addEventListener('message', function (e) {
            console.log(e.data);
        });

    })
</script>
</body>
</html>

在 运行 微服务之后(启动时没有中断)使用:

gradle bootRun

输出:

Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-05-06 14:46:41.393  INFO 39122 --- [           main] com.reactive.RsWebSocketsApplication     : Starting RsWebSocketsApplication on Porsche959.local with PID 39122 (/Users/pnwlover/rswebsockets/build/classes/java/main started by pnwlover in /Users/pnwlover/rswebsockets)
2020-05-06 14:46:41.395  INFO 39122 --- [           main] com.reactive.RsWebSocketsApplication     : No active profile set, falling back to default profiles: default
2020-05-06 14:46:42.126  INFO 39122 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-05-06 14:46:42.129  INFO 39122 --- [           main] com.reactive.RsWebSocketsApplication     : Started RsWebSocketsApplication in 0.941 seconds (JVM running for 1.579)

打开 Google Chrome 并启动了以下 URL:

http://localhost:8080/ws.html

打开 Google Chrome 开发人员工具,方法是查看页面源代码,然后单击“控制台”选项卡:


Google Chome 开发者工具 - 源选项卡:


我可能做错了什么?

通过将 @Configuration 放在 WebSocketConfig class 上方使其工作:

@Configuration
public class WebSocketConfig { 

    // Methods omitted for brevity.
}