返回

如何在 Spring WebSocket 中使用 `@SendToUser` 发送消息到特定用户?

java

使用 @SendToUser 发送消息到特定用户:问题与解决方案

简介

在 Spring WebSocket 中,@SendToUser 注解允许我们将消息发送到特定用户。与 @SendTo 不同,它使用用户 ID 而不是频道作为目的地。本文将探讨使用 @SendToUser 时可能遇到的问题及其解决方案。

问题:@SendToUser 无法收到消息

原因:

  • 用户订阅错误: 客户端未订阅 /user/topic/private-messages 频道,该频道用于接收 @SendToUser 发送的消息。
  • 用户 ID 不正确: @SendToUser 注解要求使用正确用户 ID 作为目的地。
  • 配置错误: WebSocket 配置可能未正确启用用户目的地前缀或注册目的地前缀。

解决方案

1. 客户端订阅

客户端必须订阅 /user/topic/private-messages 频道才能收到消息。这可以通过使用 JavaScript 库(如 SockJS 和 STOMP)来实现。

2. 正确的用户 ID

确保 @SendToUser 注解中使用的用户 ID 与目标用户相匹配。通常可以使用 @Principal 注解获取当前用户 ID。

3. 配置验证

WebSocketMessageBrokerConfigurer 中,启用用户目的地前缀并设置正确的目的地前缀,例如:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/websocket")
            .withSockJS()
            .setClientLibraryUrl("/webjars/sockjs-client/sockjs.min.js")
            .setHeartbeatTime(10000)
            .setDisconnectDelay(10000)
            .setAllowedOrigins("*")
            .addInterceptors(new PrincipalHandshakeInterceptor());
}

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic", "/queue");
    config.setApplicationDestinationPrefixes("/app");
    config.setUserDestinationPrefix("/user");
}

服务端代码示例

@MessageMapping("/private-message")
@SendToUser("/topic/private-messages")
public ResponseMessageDto getPrivateMessage(@Payload String message, Principal principal) {
    // 获取当前用户 ID
    String userId = principal.getName();
    
    // 发送消息到特定用户
    messagingTemplate.convertAndSendToUser(userId, "/topic/private-messages", message);
    
    return new ResponseMessageDto(HtmlUtils.htmlEscape(message));
}

客户端代码示例

stompClient.connect({}, function (frame) {
    // 订阅 `/user/topic/private-messages` 频道
    stompClient.subscribe('/user/topic/private-messages', function (message) {
        alert(message);
    })
});

常见问题解答

  1. 为什么 @SendToUser 要求用户 ID 而不是频道?

    • @SendToUser 允许将消息发送到特定用户,而不必依赖广播频道。这在发送个人或一对一消息时非常有用。
  2. 如何在服务端代码中获取用户 ID?

    • 可以使用 @Principal 注解或通过 STOMP 握手拦截器来获取当前用户 ID。
  3. 如何处理错误的用户 ID?

    • 服务器端代码应验证用户 ID,如果无效,则返回适当的错误响应。
  4. @SendToUser 是否可以与其他目的地前缀一起使用?

    • 是的,可以设置多个目的地前缀,例如 "/user" 和 "/topic"。
  5. 使用 @SendToUser 发送消息有什么性能影响?

    • 由于 @SendToUser 将消息路由到特定用户,因此它的性能开销可能高于广播消息。