Spring boot 限制特定浏览器选项卡上的@SendToUser广播

Spring boot 限制特定浏览器选项卡上的@SendToUser广播,spring-boot,websocket,stomp,Spring Boot,Websocket,Stomp,我正在Springboot上使用STOMPwebsocket,希望将广播限制在特定页面。以下是我的流程: User将消息填充到HTML输入中 浏览器将通过STOMP客户端发送消息 服务器接收消息并对其进行验证。如果消息有效,它将广播到用户处理的所有制表符,user已发出消息。如果无效,它将仅将错误消息发送回发送该消息的特定浏览器选项卡,而不会与其他选项卡一起发送,即使这些选项卡具有相同的用户登录 虽然我不能限制将错误消息发送到某个特定选项卡,但它始终将错误消息广播到共享同一用户的所有选项卡,我已

我正在Springboot上使用STOMP
websocket
,希望将广播限制在特定页面。以下是我的流程:

  • User
    将消息填充到HTML输入中
  • 浏览器将通过STOMP客户端发送消息
  • 服务器接收消息并对其进行验证。如果消息有效,它将广播到用户处理的所有制表符,
    user
    已发出消息。如果无效,它将仅将错误消息发送回发送该消息的特定浏览器选项卡,而不会与其他选项卡一起发送,即使这些选项卡具有相同的
    用户
    登录
  • 虽然我不能限制将错误消息发送到某个特定选项卡,但它始终将错误消息广播到共享同一
    用户的所有选项卡,我已经完成了部分工作。这是我的初始代码:

    @MessageMapping("/api/secure/message")
    @SendToUser("/api/secure/broadcast")
    public HttpEntity createMessage(Message message, Authentication authentication) throws Exception {
        Set<String> errors = TreeSet<String>();
        // Process Message message and add every exceptions encountered to Set errors.
        boolean valid = (errors.size() > 0);
        if(valid) {
            // Broadcast to all.
            return new ResponseEntity(message, HttpStatus.OK);
        }
        else {
            // Send the message to that particular tab only.
            return new ResponseEntity(errors, HttpStatus.UNPROCESSABLE_ENTITY);
        }
    }
    
    @MessageMapping(“/api/secure/message”)
    @发送程序(“/api/secure/broadcast”)
    公共HttpEntity createMessage(消息消息、身份验证)引发异常{
    设置错误=树集();
    //处理消息并添加遇到的每个异常以设置错误。
    布尔有效=(errors.size()>0);
    如果(有效){
    //向所有人广播。
    返回新的响应属性(消息,HttpStatus.OK);
    }
    否则{
    //仅将消息发送到该特定选项卡。
    返回新的ResponseEntity(错误,HttpStatus.UNPROCESSABLE_实体);
    }
    }
    

    这可以通过
    websocket
    实现吗?或者我应该返回到
    XHR

    ,每个选项卡都将创建一个新的websocket会话,因此您的stomp会话id也将不同。因此,我们可以决定是发送到特定会话还是发送到特定用户的所有会话

    @Autowired
    private SimpMessagingTemplate template;
    ....
    @MessageMapping(...)
    public void sendMessage(Message<?> message...) {
     .....
     StompHeaderAccessor headerAccessor = 
     StompHeaderAccessor.wrap(message);
     String sessionId = headerAccessor.getSessionId();
     ....
     if(valid) {
       //Not specifying session Id so sends all users of 
        <user_name>
       template.cnvertAndSendToUser(<user_name>, 
       <destination>, <payload>)
     }
     else {
      SimpMessageHeaderAccessor headerAccessor = 
      
      SimpMessagingHeaderAccessor.create(SimpMessageType.MESSAGE);
      headerAccessor.setSessionId(sessionId);
    
      //This will send it to particular session.
      template.convertAndSendToUser(<user_name>, 
                       <destination>, <payload>,  
              headerAccessor.getMessageHeaders());
      }
    }
    
    @Autowired
    私有SimpMessagingTemplate;
    ....
    @消息映射(…)
    公共无效发送消息(消息消息…){
    .....
    StompHeaderAccessor headerAccessor=
    StompHeaderAccessor.wrap(消息);
    字符串sessionId=headerAccessor.getSessionId();
    ....
    如果(有效){
    //未指定会话Id,因此会发送会话的所有用户
    模板.cnvertAndSendToUser(,
    , )
    }
    否则{
    SimpMessageHeaderAccessor headerAccessor=
    创建(SimpMessageType.MESSAGE);
    headerAccessor.setSessionId(会话ID);
    //这将把它发送到特定会话。
    template.convertAndSendToUser(,
    ,  
    headerAccessor.getMessageHeaders());
    }
    }
    
    有用的参考资料:


  • User@Srinivas是一个很好的参考起点。我已经用我的工作代码修改了问题中的代码块:

    // inject the [messagingTemplate] bean.
    // class org.springframework.messaging.simp.SimpMessagingTemplate
    @Autowired
    private SimpMessagingTemplate messagingTemplate;
    
    @MessageMapping("/api/secure/message")
    // Remove the @SendToUser annotation and change return type to void.
    // @SendToUser("/api/secure/broadcast")
    // public HttpEntity createMessage(Message message…
    public void createMessage(Message message, Authentication authentication) throws Exception {
        Set<String> errors = TreeSet<String>();
        // Process Message message and add every exceptions encountered to Set errors.
        boolean valid = (errors.size() > 0);
        if(valid) {
            // Broadcast to all.
            
            // Instead of returning to send the message, use the [messagingTemplate] instead.
            // return new ResponseEntity(message, HttpStatus.OK);
            messagingTemplate.convertAndSendToUser("/api/secure/broadcast", errors);
        }
        else {
            // Send the message to that particular tab only.
            
            // Each STOMP WebSocket connection has a unique ID that effectively differentiate
            // it to the other browser tabs. Retrieve that ID so we can target that specific
            // tab to send our error message with.
    
            // class org.springframework.messaging.simp.stomp.StompHeaderAccessor
            StompHeaderAccessor stompHeaderAccessor = StompHeaderAccessor.wrap(message);
            String sessionId = stompHeaderAccessor.getSessionId();
    
            // class org.springframework.messaging.simp.SimpMessageHeaderAccessor
            // class org.springframework.messaging.simp.SimpMessageType
            // class org.springframework.util.MimeType
            // class java.nio.charset.StandardCharsets
            SimpMessageHeaderAccessor simpHeaderAccessor =
                SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
            simpHeaderAccessor.setSessionId(sessionId);
            simpHeaderAccessor.setContentType(new MimeType("application", "json",
                StandardCharsets.UTF_8));
            simpHeaderAccessor.setLeaveMutable(true);
    
            // Instead of returning to send the message, use the [messagingTemplate] instead.
            // It will ensure that it will only broadcast the message to the specific
            // STOMP WebSocket sessionId.
            // return new ResponseEntity(errors, HttpStatus.UNPROCESSABLE_ENTITY);
            messagingTemplate.convertAndSendToUser(sessionId, "/api/secure/broadcast",
                errors, simpHeaderAccessor.getMessageHeaders());
        }
    }
    
    //注入[messagingTemplate]bean。
    //类org.springframework.messaging.simp.SimpMessagingTemplate
    @自动连线
    私有SimpMessagingTemplate消息模板;
    @MessageMapping(“/api/secure/message”)
    //删除@SendToUser注释并将返回类型更改为void。
    //@SendToUser(“/api/secure/broadcast”)
    //公共HttpEntity createMessage(消息消息…
    public void createMessage(消息消息、身份验证)引发异常{
    设置错误=树集();
    //处理消息并添加遇到的每个异常以设置错误。
    布尔有效=(errors.size()>0);
    如果(有效){
    //向所有人广播。
    //使用[messagingTemplate]代替返回发送消息。
    //返回新的响应属性(消息,HttpStatus.OK);
    messagingTemplate.convertAndSendToUser(“/api/secure/broadcast”,错误);
    }
    否则{
    //仅将消息发送到该特定选项卡。
    //每个STOMP WebSocket连接都有一个唯一的ID,可以有效区分
    //它将指向其他浏览器选项卡。检索该ID以便我们可以针对该特定选项卡
    //选项卡以发送错误消息。
    //类org.springframework.messaging.simp.stomp.StompHeaderAccessor
    StompHeaderAccessor StompHeaderAccessor=StompHeaderAccessor.wrap(消息);
    字符串sessionId=stompHeaderAccessor.getSessionId();
    //类org.springframework.messaging.simp.SimpMessageHeaderAccessor
    //类org.springframework.messaging.simp.SimpMessageType
    //类org.springframework.util.MimeType
    //类java.nio.charset.StandardCharset
    SimpMessageHeaderAccessor simpHeaderAccessor=
    创建(SimpMessageType.MESSAGE);
    simpHeaderAccessor.setSessionId(sessionId);
    setContentType(新的MimeType(“应用程序”、“json”),
    标准字符集(UTF_8));
    simpHeaderAccessor.setLeaveMutable(true);
    //使用[messagingTemplate]代替返回发送消息。
    //它将确保只向特定用户广播消息
    //踩踏网匣会话ID。
    //返回新的ResponseEntity(错误,HttpStatus.UNPROCESSABLE_实体);
    messagingTemplate.convertAndSendToUser(会话ID,“/api/secure/broadcast”,
    错误,simpHeaderAccessor.getMessageHeaders());
    }
    }
    

    如果在控制器方法参数上使用
    @ResponseBody@Valid
    ,则必须将逻辑线移动到
    ControllerAdvice
    exceptionHandler()

    变量
    watchesSubscription
    声明在哪里?抱歉,我把它放错了,它应该是我之前得到的sessionId变量,请现在检查。