我用
创建了一个小型 Spring Boot 服务器<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>3.3.4</version>
</dependency>
拥有 WebSocketConfigurer
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ServerWebSocketHandler(), "/socket");
}
在 ServerWebSocketHandler 中
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("Connection open");
TextMessage message = new TextMessage("initial message from server");
log.info("Sending message: {}", message);
session.sendMessage(message);
}
当我使用 Postman 创建到
ws://localhost:8080/socket
的 Websocket 连接时,连接已建立,并且我看到了预期的消息和日志条目。
但是,尝试从 React.js 应用程序内部建立连接失败。
这是来自 App.tsx:
function App() {
const ws : MutableRefObject<WebSocket|null> = useRef(null);
useEffect(() => {
ws.current = new WebSocket("ws://localhost:8080/socket");
ws.current.onopen = () => console.log("ws open");
ws.current.onclose = () => console.log("ws closed");
const wsCurrent = ws.current;
return () => {
wsCurrent.close();
};
}, []);
useEffect(() => {
if (!ws.current) return;
ws.current.onmessage = e => {
const message = JSON.parse(e.data);
console.log("e", message);
};
}, []);
return (
<div className="App">
<div>
Hey world.
</div>
</div>
);
}
和日志条目:
App.tsx:15 WebSocket connection to 'ws://localhost:8080/socket' failed: WebSocket is closed before the connection is established.
App.tsx:10 ws closed
App.tsx:8 WebSocket connection to 'ws://localhost:8080/socket' failed:
(anonymous) @ App.tsx:8
commitHookEffectListMount @ react-dom.development.js:23189
invokePassiveEffectMountInDEV @ react-dom.development.js:25193
invokeEffectsInDev @ react-dom.development.js:27390
commitDoubleInvokeEffectsInDEV @ react-dom.development.js:27369
flushPassiveEffectsImpl @ react-dom.development.js:27095
flushPassiveEffects @ react-dom.development.js:27023
(anonymous) @ react-dom.development.js:26808
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
这些尝试的服务器日志中没有任何内容...
那么为什么Postman可以连接到服务器而react却失败呢?我错过了什么?
编辑:深入挖掘一下,我注意到来自 React 的请求失败并出现 403 Forbidden... 但我看不出是什么导致一个请求失败而另一个请求成功..
GET /socket HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Sec-WebSocket-Version: 13
Origin: http://localhost:3000
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: -----------------------==
DNT: 1
Sec-GPC: 1
Connection: keep-alive, Upgrade
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: same-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
(Websocket 密钥已删除)
所以问题是react.js请求在spring-boot应用程序中触发了cors,而postman请求省略了origin header,从而跳过了cors检查。
现在我将 WebSocketConfigurer 更改为
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ServerWebSocketHandler(), "/socket")
.setAllowedOrigins("http://localhost:3000");
}
避免这个问题。