I am using STOMP websocket
on Springboot and want to limit a broadcast to a particular page. Here is my process:
User
fills up a message to a HTML input.User
has made a message. If it is invalid, it will send back the error message only to that particular browser tab that sent the message and not with other tabs even if those tabs have the same User
logged in.I already made some parts of it work although I couldn't limit sending the error message to a particular tab, it always broadcast the the error message to all tab sharing the same User
. Here is my initial code:
@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);
}
}
Is this achievable through websocket
? Or should I return back to XHR
?
User @Srinivas made a good starting reference point. I have modified the code block in my question with my working code:
// 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());
}
}
If you are using @ResponseBody @Valid
on your controller method parameter, you'll have to move the logic lines to your ControllerAdvice
exceptionHandler()
.