Search code examples
websocketspring-websocketstompjava-websocket

How to pass message to controller by @MessageMapping with specified user url?


I have such problem. When i try to send message from client side to server, it doesn't match with my @MessageMapping methods. I don't know how to intercept messages on controller layer.

Client side sends message (it's react-stomp that uses sockjs):

move = (move) => {
    this.clientRef.sendMessage("/user/${this.state.opponentId}/queue/move", JSON.stringify(move))
};

Server side. WebSocketConfig:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/handler")
                .setHandshakeHandler(new CustomHandshakeHandler())
                .setAllowedOrigins("http://localhost:3000")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry brokerRegistry) {
        brokerRegistry.setApplicationDestinationPrefixes("/app");
        brokerRegistry.enableSimpleBroker("/topic", "/queue", "/user");
    }

    @EventListener
    void handleSessionConnectedEvent(SessionConnectedEvent event) {
        StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new MyChannelInterceptor());
    }

}

I also added interceptor class to check path of incomming message:

public class MyChannelInterceptor implements ChannelInterceptor {

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
        return message;
    }
}

On debugging of MyChannelInterceptor i see message with payload and headers. There is simpDestination header with such value:

/user/baedde36-0f9e-4fa5-b8d7-687db1dbcd67/queue/move

What @MessageMapping value should i write to handle messages from specified users? This message succesfully gets to frontside by subscription on this topic but doesn't stay on any controller:

`/user/${message}/queue/move`

I just want to handle messages on server side but i can't catch it there.


Solution

  • Okay. As i understood there is 3 ways to handle websocket messages:

    /app - handles with controller
    /user - handles with broker, sends messages to specific users
    /topic - broadcast to topic for all subscribers
    

    In my situation i just need to create json object with userId, receiverId and message. On server side add DTO class and get it as attribute in my controller method.

    Solution:

    move = (move) => {
        let moveDto = {move: move, userId: this.state.userId, opponentId: this.state.opponentId}
        this.clientRef.sendMessage(`/app/move`, JSON.stringify(moveDto))
    };
    

    Server side:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class MoveDto {
        private String userId;
        private String opponentId;
        private int move;
    }
    

    Controller class:

    @RestController
    public class GameController {
    
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;
    
    ...//some code here
    
    @MessageMapping("/move")
    public void message(MoveDto moveDto) {
    String userMessage= "foo";
    String opponentMessage = "bar";
    
     simpMessagingTemplate.convertAndSendToUser(
                moveDto.getUserId(), "/queue/message", userMessage);
    
     simpMessagingTemplate.convertAndSendToUser(
                moveDto.getOpponentId(), "/queue/message", opponentMessage );
    }