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.
}
我正在学习使用 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.
}