0

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);
    }
}
Jose Ortiz
  • 41
  • 6
  • Can you check what is sent from front to back though socket? Do you receive anything in controller on back? On back you are using STOMP subprotocol and I am not sure does you frontend library sends STOMP messages to back. – Boris Jan 17 '21 at 18:48
  • @Boris seems socket io doesnt use STOMP protocol. The problem is, with STOMP I can not send the Authorization header – Jose Ortiz Jan 17 '21 at 20:01
  • 1
    If you want to use backed as you wrote above, you will need to use STOMP library, such as https://github.com/stomp-js/stompjs. You could check for authorization header during CONNECT message in stomp. If you want to use socket.io on front, you will need something similar on back. Check this question: https://stackoverflow.com/a/64916962/4770761. Other solution is to use raw Websocket (use can still use SockJS) and use custom format of messages to get logic that you want to implement. In spring you can implement TextWebSocketHandler or BinaryWebSocketHandler, but I still recommend STOMP. – Boris Jan 18 '21 at 12:50

0 Answers0