Implementing WebSocket in Projects

· 3 min read

Recently, our project had a requirement where users open a tab from our System A to operate System B, and when they click a certain operation button, it calls back our system’s API to change some data. We needed to execute a series of actions when events occur, such as page refreshing. The underlying requirement actually tells us that our system needs the ability for the backend to actively push messages to the frontend. How to achieve this? WebSocket.

So, I began building support for it.

Tech Stack

First, let me introduce the current project’s tech stack:

  • React v16.4.2
  • SpringBoot v2.1.6.RELEASE

WebSocket technology itself is quite mature, so configuration and development for these two areas isn’t very difficult. Here are the key code blocks:

SpringBoot

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS();
    }

}
@RestController
public class GreetingController {
    private final SimpMessagingTemplate simpMessagingTemplate;

    public GreetingController(SimpMessagingTemplate simpMessagingTemplate) {
        this.simpMessagingTemplate = simpMessagingTemplate;
    }

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        System.out.println("Request: get message: " + message);
        return new Greeting("Response: Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
    }

    @GetMapping("/broadcast")
    public String sendBroadcastCommand(@RequestParam String command) {
        simpMessagingTemplate.convertAndSend("/topic/broadcast", command);
        return command;
    }
}

React

<script src="static/js/stomp.min.js"></script>
<script src="static/js/sockjs-1.0.0.min.js"></script>
function watchBroadcast() {
  const socket = new SockJS('/websocket');
  const client = Stomp.over(socket);
  client.connect(
    {},
    () => {
      client.subscribe('/topic/broadcast', res => {
       console.log(res.body)
        });
    }
  );
}

Result

When the /broadcast API is called, the backend sends messages to the frontend.

It’s that simple, but there are still some pitfalls in actual development. Let’s continue:

Important Notes

  1. If the backend has interceptors, remember to set up a whitelist for paths like /websocket, otherwise you’ll get a 302 error.
  2. SocketJS versions need to match between frontend and backend. Here my backend SocketJS version is 1.0.0, so the frontend sockjs must also be 1.0.0, otherwise compatibility errors will occur.
  3. During development, I encountered an issue where everything worked fine locally but returned a 500 error when deployed. The reason was that deployment used spring-boot-starter-undertow, which wasn’t the container used locally. I ended up removing it. Note that undertow should be supported, but it might be a version issue.
  4. In my scenario, the backend API is called and then sends messages to the frontend, so I’m using GetMapping and simpMessagingTemplate together. If you need two-way communication, the first example is sufficient. Testing with both GetMapping and SendTo annotations didn’t work, so it seems this approach is necessary for this scenario.
  5. WebSocket doesn’t add new port numbers, still using 80 and 443, but since communication uses the WS protocol, container layers like Nginx and browser clients need to support it.
  6. Multiple connections can be established for the same service, and multiple subscriptions can be made for the same event, so be careful to avoid duplicate processing issues and wasted resources.

Conclusion

  • Why do we need WebSocket? Consider this statement: “HTTP protocol has a flaw: communication can only be initiated by the client, while WebSocket enables two-way communication between web frontend and backend.” So when to use it is quite clear.
  • After implementing WebSocket in our project, similar requirements became much simpler (such as server-side directional collection of client information), making it worthwhile.

References

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover