Make it work

Using Spring WebSocket (STOMP) application with Vue.js

Overview

In this article, you will learn how to build a Spring application with WebSocket (STOMP) and consume it with Vue.js on the frontend. If you are new to this topic I really suggest you go step by step in this guide first, because I’m not going into the very detail how everything works. The example below is based on this guide, so after completing it you can reuse your code. The full source code for this post is available in the GitHub project.

Spring WebSocket - backend

First, let’s initialize our project with Spring Initializr. Add a Web and WebSocket dependency and open generated project with your favorite IDE. Now create the HelloMessage and Greeting class like below:

public class HelloMessage {

    private String name;

    public HelloMessage() {
    }

    public HelloMessage(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Greeting {

    private String content;

    public Greeting() {
    }

    public Greeting(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

}

The HelloMessage class will be used for request and the Greeting for the response. To handle this we need to write our GreetingController class:

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;

@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        Thread.sleep(1000); // simulated delay
        return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
    }

}

In the next step, we are going to add a configuration class to enable WebSocket in our application.

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@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("/gs-guide-websocket").withSockJS();
    }

}

And that’s it. Our backend application is ready to run.

Testing WebSocket application

To test our application, we need an external tool. I’m going to use the Apic Chrome extension because it supports STOMP. Our Socket URL will be http://localhost:8080/gs-guide-websocket because we registered that endpoint earlier and Spring run at 8080 port by default. So, select Stomp, click Connect and it looks like it’s connected… isn’t it? Unfortunately, it isn’t, and we can see it in the browser console.

WebSocket connection to 'ws://localhost:8080/gs-guide-websocket/859/hxqviaxx/websocket' failed: Error during WebSocket handshake: Unexpected response code: 403
POST http://localhost:8080/gs-guide-websocket/859/hdp0qoeb/xhr_streaming?t=1563971159353 403
Web Socket Opened...
stomp.min.js:8 >>> CONNECT
accept-version:1.1,1.0
heart-beat:60000,0
POST http://localhost:8080/gs-guide-websocket/859/nsexfxod/xhr_send?t=1563971159370 403
Whoops! Lost connection to http://localhost:8080/gs-guide-websocket
tab-socket-controller.js:148 Whoops! Lost connection to http://localhost:8080/gs-guide-websocket

During the WebSocket handshake, we’ve got unexpected response code 403, which means Forbidden. Then SockJS attempts to connect with the alternate transports, like xhr_streaming, but still got the 403 code. This is because we try to connect from outside of localhost and our request doesn’t pass access control check. To enable CORS we can simple add allowed origins in WebSocketConfig:

@Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/gs-guide-websocket")
                .setAllowedOrigins("http://localhost:8081",
                        "chrome-extension://ggnhohnkfcpcanfekomdkjffnfcjnjam")
                .withSockJS();
    }

As you can see I’ve added two hosts, the first one for our frontend application at 8081 port, and the second, for our Google Chrome extension. After restarting the application, we should be able to establish the connection. Then we can fill the Apic’s form like below:

Subscription URL:/topic/greetings
Messages Destination Queue:/app/hello
Content to be sent:{"name": "kojot"}

After one second of sending a message, we see the response
content: "Hello, kojot!". The application works as expected.

Vue.js - frontend

After creating a new project with Vue CLI, we need to install a few things:

  • SockJS with npm install sockjs-client
  • STOMP with npm install webstomp-client
  • Bootstrap and jQuery npm install bootstrap@3 npm install jquery just for layout

To setup Bootstrap open main.js file and add these lines:

import $ from "jquery";
window.jQuery = window.$ = $;
require("bootstrap/dist/css/bootstrap.min.css");
require("bootstrap/dist/js/bootstrap.min.js");

Now we can add a new component called WebsocketGreetings:






Change the Home.vue to:​





and run the project with npm run serve.  It should start at 8081 port by default. 

You can test your application now with multiple browsers. If you have any questions please don’t hesitate to share a comment below. Remember that full source of the code is available on GitHub.

4 Comments

  • CPicou

    Thank you, you solved my problem for the connection thanks to setAllowedOrigins.
    But now, my problem is that the client don’t receive the message. I’ve put a breakpoint in the controller and it’s ok. So it seems that the subscription doesn’t work. Do you have any idea ?

    • kojot

      Hello CPicau, thanks for the feedback. First, please clone my project https://github.com/kkojot/spring-vue-websocket-stomp and check that you still have the problem. If so, please look into the browser console and paste the log here. If you see the correct `HelloMessage` value in the `greeting` method of the controller, it means that the connection and Jackson parsing works fine. The problem must be in Subscription URL, `configureMessageBroker` or `@SendTo` annotation. And did you try to test it with Apic either?

Leave a Reply

Your email address will not be published.