STOMP WebSocket消息发送正常但前端无法接收问题求助
我搭建了简易的STOMP WebSocket服务,后端能正常接收并处理消息,但前端订阅后收不到任何消息,前后端无报错日志。已尝试更换浏览器、用IP替代localhost,问题依旧。相关代码如下:
WebSocket配置类
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/app"); registry.setUserDestinationPrefix("/user"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns("*") .withSockJS() .setInterceptors(new HttpSessionHandshakeInterceptor()); } @Override public boolean configureMessageConverters(List<MessageConverter> messageConverters) { DefaultContentTypeResolver resolver = new DefaultContentTypeResolver(); resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON); MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); converter.setObjectMapper(new ObjectMapper()); converter.setContentTypeResolver(resolver); messageConverters.add(converter); return false; } }
消息实体类
@RequiredArgsConstructor @Getter @AllArgsConstructor public class ChatMessageForm { private String id; //private LocalDateTime timeStamp; private String content; private String recipientId; private String senderId; }
控制器
@Controller @RequiredArgsConstructor public class ChatController { private final ChatService chatService; @MessageMapping("/chat") public void handleChatMessage(@Payload ChatMessageForm message, SimpMessageHeaderAccessor headerAccessor) { chatService.handleChatMessage(new ChatMessage( LocalDateTime.now(), message.getContent(), message.getRecipientId(), message.getSenderId() )); } }
服务实现类
@Service @RequiredArgsConstructor public class ChatServiceImpl implements ChatService { private final SimpMessagingTemplate messagingTemplate; @Override public void handleChatMessage(ChatMessage message) { messagingTemplate.convertAndSendToUser( message.getRecipientId(), "queue/messages", new ChatNotification( "", message.getSenderId(), message.getRecipientId(), message.getContent() ) ); } }
前端连接代码
const connectWebSocket = () => { if (!user?.id) { console.error('User ID is not defined'); return; } client.current = new Client({ brokerURL: 'ws://127.0.0.1:8080/ws/websocket', debug: function (str) { console.log(str); }, onConnect: () => { console.log('WebSocket connection established'); client.current?.subscribe(`/user/${user.id}/queue/messages`, (message) => { const receivedMessage: Message = JSON.parse(message.body); console.log("Received message: ", receivedMessage); updateChatWithNewMessage(receivedMessage); }); }, onStompError: (frame) => { console.error('Broker reported error: ' + frame.headers['message']); console.error('Additional details: ' + frame.body); }, onWebSocketClose: () => { console.log('WebSocket connection closed'); } }); client.current.activate(); };
排查方向及解决步骤
1. 消息代理未监听队列前缀
你的配置中enableSimpleBroker只启用了/topic前缀,但用户私信使用的/queue前缀不在监听范围内。Spring的convertAndSendToUser会生成/user/{recipientId}/queue/messages地址,若消息代理没配置监听/queue,消息无法被路由到客户端。
修复: 修改configureMessageBroker方法,添加/queue到代理监听列表:
@Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic", "/queue"); registry.setApplicationDestinationPrefixes("/app"); registry.setUserDestinationPrefix("/user"); }
2. 消息序列化异常
检查ChatNotification实体是否具备正确的序列化条件:
- 确保添加了
@Getter、@Setter或@Data(Lombok注解),否则Jackson无法序列化字段; - 若使用自定义
ObjectMapper,确认配置支持所需的字段类型(比如Java 8时间类,若存在的话)。
3. 用户会话未绑定正确ID
convertAndSendToUser默认依赖Principal关联用户与WebSocket会话,若后端无法识别用户ID对应的会话,消息会被静默丢弃。
验证: 在ChatController的handleChatMessage中打印headerAccessor.getUser(),若返回null说明会话未绑定用户。
修复(无认证场景):
在握手时手动绑定用户ID到会话:
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns("*") .withSockJS() .setInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // 从请求参数获取用户ID,需前端连接时携带 String userId = ((ServletServerHttpRequest) request).getServletRequest().getParameter("userId"); if (userId != null) { attributes.put("userId", userId); // 设置Principal供框架识别用户 request = new ServerHttpRequestDecorator(request) { @Override public Principal getPrincipal() { return () -> userId; } }; } return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {} }); }
前端连接时携带用户ID:
brokerURL: `ws://127.0.0.1:8080/ws/websocket?userId=${user.id}`,
4. 订阅地址与发送地址不匹配
确认前端user.id与后端message.getRecipientId()的格式完全一致(大小写、空格、特殊字符等)。可在前端订阅时打印地址,后端发送时打印目标地址,对比是否完全匹配。
5. SockJS连接方式问题
若浏览器WebSocket支持存在兼容性问题,改用SockJS原生连接方式:
// 需先引入SockJS库:<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script> client.current = new Client({ webSocketFactory: () => new SockJS('http://127.0.0.1:8080/ws'), debug: function (str) { console.log(str); }, onConnect: () => { console.log('WebSocket connection established'); client.current?.subscribe(`/user/${user.id}/queue/messages`, (message) => { const receivedMessage: Message = JSON.parse(message.body); console.log("Received message: ", receivedMessage); updateChatWithNewMessage(receivedMessage); }); }, // 其他错误处理配置不变 });
内容的提问来源于stack exchange,提问作者Darlyn

