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