rsocket-js routing fireAndForget to Spring Boot @MessageMapping

1k views Asked by At

As I understand RSocket-JS supports routing messages using encodeCompositeMetadata and encodeRoute, however, I cannot get the server to accept a fireAndForget message. The server constantly logs the following message:

o.s.m.r.a.support.RSocketMessageHandler : No handler for fireAndForget to ''

This is the server mapping I am trying to trigger:

    @Controller
    public class MockController {
        private static final Logger LOGGER = LoggerFactory.getLogger(MockController.class);
    
        @MessageMapping("fire-and-forget")
        public Mono<Void> fireAndForget(MockData mockData) {
            LOGGER.info("fireAndForget: {}", mockData);
            return Mono.empty();
        }
    }

This is the TypeScript code that's trying to make the connection:

    client.connect().subscribe({
        onComplete: socket => {
            console.log("Connected to socket!")
            socket.fireAndForget({
                data: { someData: "Hello world!" },
                metadata: encodeCompositeMetadata([[MESSAGE_RSOCKET_ROUTING, encodeRoute("fire-and-forget")]])
            });
        },
        onError: error => console.error(error),
        onSubscribe: cancel => {/* call cancel() to abort */ }
    });

I've also tried adding the route in other ways (metadata: String.fromCharCode('route'.length)+'route') I found on the internet, but none seem to work.

What do I need to do to format the route in a way that the Spring Boot server recognizes it and can route the message correctly?

1

There are 1 answers

7
Oleh Dokuka On BEST ANSWER

Binary only communication when using CompositeMetadata

Please make sure that you have configured your ClientTransport with binary codecs as follows:

new RSocketWebSocketClient(
    {
      url: 'ws://<host>:<port>'
    },
    BufferEncoders,
  ),

Having Binary encoders you will be able to properly send your routes using composite metadata.

Also, please make sure that you have configured metadataMimeType as:

...
const metadataMimeType = MESSAGE_RSOCKET_COMPOSITE_METADATA.string; // message/x.rsocket.composite-metadata.v0

new RSocketClient<Buffer, Buffer>({
  setup: {
    ...
    metadataMimeType,
  },
  transport: new RSocketWebSocketClient(
    {
      url: 'ws://<host>:<port>',
    },
    BufferEncoders,
  ),
});

Note, once you enabled BufferEncoders your JSONSeriallizer will not work and you would need to encode your JSON to binary yours selves ( I suggest doing that since in the future versions we will remove support of Serializers concept completely). Therefore, your request has to be adjusted as it is in the following example:

client.connect().subscribe({
        onComplete: socket => {
            console.log("Connected to socket!")
            socket.fireAndForget({
                data: Buffer.from(JSON.stringify({ someData: "Hello world!" })),
                metadata: encodeCompositeMetadata([[MESSAGE_RSOCKET_ROUTING, encodeRoute("fire-and-forget")]])
            });
        },
        onError: error => console.error(error),
        onSubscribe: cancel => {/* call cancel() to abort */ }
    });

Use @Payload annotation for your payload at spring backend

Also, to handle any data from the client and to let Spring know that the specified parameter argument is your incoming request data, you have to annotate it with the @Payload annotation:

@Controller
    public class MockController {
        private static final Logger LOGGER = LoggerFactory.getLogger(MockController.class);
    
        @MessageMapping("fire-and-forget")
        public Mono<Void> fireAndForget(@Payload MockData mockData) {
            LOGGER.info("fireAndForget: {}", mockData);
            return Mono.empty();
        }
    }