So I have an API using spring boot, and I try to implement a WebSocket endpoint where users get subscribed when they log in, and where they listen to notifications of different entities creation. Supposedly my WebSocket is working on the backend, here is my code.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/points_notification")
.setAllowedOrigins("localhost:3000")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
@Controller
public class NotificationsController {
@Autowired private NotificationDispatcher dispatcher;
@MessageMapping("/start")
public void start(StompHeaderAccessor stompHeaderAccessor) {
dispatcher.add(stompHeaderAccessor.getSessionId());
System.out.println("GOT a session! " + stompHeaderAccessor.getSessionId());
}
@MessageMapping("/stop")
public void stop(StompHeaderAccessor stompHeaderAccessor) {
dispatcher.remove(stompHeaderAccessor.getSessionId());
}
}
@Service
public class NotificationDispatcher {
@Autowired
private SimpMessagingTemplate template;
private final Set<String> listeners = new HashSet<>();
public <T extends Serializable> void dispatch(T addedObj) {
for (String listener : listeners) {
LOG.info("Sending notification to " + listener);
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(listener);
headerAccessor.setLeaveMutable(true);
template.convertAndSendToUser(
listener,
"/topic/item",
new ObjWithMsgResponse<T>("Point added by: " + listener, addedObj),
headerAccessor.getMessageHeaders());
}
}
//getters and event listeners
}
of course the dispatch()
are injected and called when the creation of entities.
In the front I'm using react and trying to handle the socket connection with socket.io-client
, I'm a little new with this, so I don't really understand how the emit
and on
methods work, because in the documentation they show that you call and eventName, but in spring the endpoints are using the URL, I can not find anywhere how to ping a URL and not an eventName.
I also tried to use SockJS
that works like a charm but the endpoint needs Authentication and with this, I can not add that header to the socket request, which soocket.io permits me.
here is my solution, which only connects but doesn't receive or emit any request.
class SocketManager {
socketRef: Socket | undefined = undefined;
startListening(authToken: String) {
this.socketRef = io('http://localhost:11000/weblab4/', {
path: "/weblab4" + '/points_notification',
transports: ['websocket'],
extraHeaders: {
Authorization: "Bearer " + authToken
}
});
this.socketRef.emit('/app/start', {});
this.socketRef.on('/user/topic/item', (data: INewPointNotification) => {
console.log("received data from socket: " + data);
toast.success(' ' + data.msg + data.point, {/*toast props*/});
});
console.log("socket connected", this.socketRef);
}
stopListening() {
if (this.socketRef !== undefined)
this.socketRef.disconnect();
console.log("socket disconnected", this.socketRef);
}
}