Angular 12 WebSocket example with Spring Boot WebSocket Server | SockJS + STOMP

https://grokonez.com/angular-12-springboot-websocket

The WebSocket protocol provides new capability for web applications: full-duplex, two-way communication. So in the system where the client and server need to exchange data at high frequency and with low latency, WebSocket is the best solution. In this tutorial, we're gonna create an Angular 12 WebSocket Client to send/receive message with a Spring Boot Server.

WebSocket Application

Flow of messages

We create a Spring WebSocket Application with the flow of messages:

spring-websocket-architecture-angular-6-websocket-client

  • WebSocket clients connect to the WebSocket endpoint at /gkz-stomp-endpoint
  • Subscriptions to /topic/hi pass through the response channel, then are forwarded to the In-memory broker (Simple Broker).
  • User objects sent to /gkz/hello pass through the request channel then are forwarded to the spring WebController. WebController will handle User objects by @MessageMapping and transform to Hello messages then use @SendTo returns the messages to /topic/hi through the brokerChannel.

@MessageMapping("/hello")
@SendTo("/topic/hi")
public Hello greeting(User user) throws Exception {
    return new Hello(...);
}
  • The Simple Broker broadcasts messages to subscribers through the response channel.

Server side

In server side, we use SockJS and STOMP for our application.

What is SockJS?
-> SockJS lets applications use a WebSocket API but falls back to non-WebSocket alternatives when necessary at runtime, without the need to change application code.

We create a simple Java configuration to enable SockJS and Stomp in Spring application:


@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
        .addEndpoint("/gkz-stomp-endpoint")
        .setAllowedOrigins("http://localhost:4200")
        .withSockJS();
    }
}

Client side

We uses {sockjs-client, stompjs} for development on Client side:


import * as Stomp from 'stompjs';
import * as SockJS from 'sockjs-client';

const _this = this;
this.stompClient.connect({}, function (frame) {
_this.stompClient.subscribe('/topic/hi', function (hello) {
_this.showGreeting(JSON.parse(hello.body).greeting);
});
});
}

connect() function uses SockJS and Stomp to open a connection to /gkz-stomp-endpoint, which is where our SockJS server is waiting for connections.

  • Disconnection:

disconnect() {
  if (this.stompClient != null) {
    this.stompClient.disconnect();
  }
}
  • Send messages:
    
    sendName() {
    this.stompClient.send(
    '/gkz/hello',
    {},
    JSON.stringify({ 'name': this.name })
    );
    }
    

    Practice

    Technologies

    – Java 8 – Maven 3.3.9 – Spring Tool Suite 3.9.0.RELEASE – Spring Boot: 2.0.4.RELEASE
  • Spring WebSocket

  • sockjs-client 1.1.5

  • @stomp/stompjs 4.0.7

  • Angular 12

  • Bootstrap

    1. Server side

    angular-6-websocket-example-spring-websocket-spring-server-structure

    1.1 Create SpringBoot project

    Using Spring Tool Suite to create a Spring Starter Project, then add dependencies:

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

    1.2 Create models

    Create 2 message models {User, Hello}

  • User:


    package com.grokonez.spring.websocket.model;

public class User {
private String name;

public User() {
}

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

public String getName() {
    return this.name;
}

}

  • Hello:
    
    package com.grokonez.spring.websocket.model;

public class Hello {
private String greeting;

public Hello() {
}

public Hello(String greeting) {
    this.greeting = greeting;
}

public void setGreeting(String greeting) {
    this.greeting = greeting;
}

public String getGreeting() {
    return this.greeting;
}

}

1.3 Configure SockJS and STOMP messaging


package com.grokonez.spring.websocket.config;

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("/gkz");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
        .addEndpoint("/gkz-stomp-endpoint")
        .setAllowedOrigins("http://localhost:4200")
        .withSockJS();
    }
}
  • @EnableWebSocketMessageBroker enables WebSocket message handling, backed by a message broker.
  • enableSimpleBroker() enables a simple memory-based message broker.
  • registerStompEndpoints() is for registering the /gkz-stomp-endpoint endpoint, enabling SockJS fallback options - an alternate transports in case WebSocket is not available. We also configure allowed Origin header values for browser clients.

    1.4 Create WebController

    
    package com.grokonez.spring.websocket.controller;

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

import com.grokonez.spring.websocket.model.Hello;
import com.grokonez.spring.websocket.model.User;

@Controller
public class WebController {

@MessageMapping("/hello")
@SendTo("/topic/hi")
public Hello greeting(User user) throws Exception {
    return new Hello("Hi, " + user.getName() + "!");
}

}
User objects are sent to /gkz/hello pass through the request channel then are forwarded to the Spring WebController.

WebController will handle User objects by @MessageMapping and transform to Hello messages then use @SendTo to return the messages to '/topic/hi' through the brokerChannel.

2. Client side

2.1 Create Angular project

  • Using Angular CLI to create new Project: ng new Angular6WebSocket.
  • Install @stomp/stompjs and socksjs:
    npm install @stomp/stompjs
    npm install sockjs-client

  • Add this code in the first line of polyfills.ts file:

    
    (window as any).global = window;
    

    2.2 Add FormsModule

    app.module.ts


    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

2.3 HTML for UI

app.component.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Spring Boot WebSocket</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>

<body>
  <div style="color: blue; text-align: center">
    <h1>{{title}}</h1>
    <h3>{{description}}</h3>
  </div>

  <div class="container" style="width: 400px; margin-top: 20px;">

    <form class="form-inline">
      <div class="form-group">
        <label for="connect">Make Connection:</label>
        <button id="connect" class="btn btn-default" type="button" [disabled]="!disabled" (click)="connect()">Connect</button>
        <button id="disconnect" class="btn btn-default" type="submit" [disabled]="disabled" (click)="disconnect()">Disconnect</button>
      </div>
    </form>

    <form class="form-inline" style="margin-top: 20px;">
      <div class="form-group">
        <label for="name">User's Name:</label>
        <input type="text" id="name" name="name" class="form-control" [(ngModel)]="name" />
      </div>
      <button id="send" class="btn btn-default" type="button" (click)="sendName()">Send</button>
    </form>

    <table id="conversation" class="table table-striped" style="margin-top: 20px;">
      <thead>
        <tr>
          <th>Greetings</th>
        </tr>
      </thead>
      <tbody *ngFor="let greeting of greetings">
        <tr>
          <td>{{greeting}}</td>
        </tr>
      </tbody>
    </table>

  </div>
</body>

</html>

2.4 WebSocket functions

app.component.ts


import { Component } from '@angular/core';
import * as Stomp from '@stomp/stompjs';
import * as SockJS from 'sockjs-client';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'grokonez';
  description = 'Angular-WebSocket Demo';

  greetings: string[] = [];
  disabled = true;
  name: string;
  private stompClient = null;

  constructor() { }

  setConnected(connected: boolean) {
    this.disabled = !connected;

    if (connected) {
      this.greetings = [];
    }
  }

  connect() {
    const socket = new SockJS('http://localhost:8080/gkz-stomp-endpoint');
    this.stompClient = Stomp.over(socket);

    const _this = this;
    this.stompClient.connect({}, function (frame) {
      _this.setConnected(true);
      console.log('Connected: ' + frame);

      _this.stompClient.subscribe('/topic/hi', function (hello) {
        _this.showGreeting(JSON.parse(hello.body).greeting);
      });
    });
  }

  disconnect() {
    if (this.stompClient != null) {
      this.stompClient.disconnect();
    }

    this.setConnected(false);
    console.log('Disconnected!');
  }

  sendName() {
    this.stompClient.send(
      '/gkz/hello',
      {},
      JSON.stringify({ 'name': this.name })
    );
  }

  showGreeting(message) {
    this.greetings.push(message);
  }
}

Run and check results

  • SpringBoot project with command-lines: mvn clean install and mvn spring-boot:run.
  • Angular project: npm install and npm start.

Open browser with url http://localhost:4200/.
Click on Connect button and send User's Name.
Then click on Disconnect button.

angular-6-websocket-example-spring-websocket-result-client

Source code

https://grokonez.com/angular-12-springboot-websocket

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

loveprogramming

545 bài viết.
97 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
1 0
Tutorial Link: (Link) (Ảnh) Django is a Pythonbased free and opensource web framework that follows the modeltemplateview architectural pattern. A...
loveprogramming viết 9 tháng trước
1 0
Male avatar
1 0
https://loizenai.com/angular11nodejspostgresqlcrudexample/ Angular 11 Node.js PostgreSQL Crud Example (Ảnh) Tutorial: “Angular 11 Node.js Postg...
loveprogramming viết 7 tháng trước
1 0
Male avatar
1 0
Angular Spring Boot jwt Authentication Example Github https://loizenai.com/angularspringbootjwt/ (Ảnh) Tutorial: ” Angular Spring Boot jwt Authe...
loveprogramming viết 7 tháng trước
1 0
Bài viết liên quan
Male avatar
0 0
https://grokonez.com/springframework/springwebsocket/createspringwebsocketapplicationspringbootsockjsstomp WebSocket – Create Spring WebSocket App...
loveprogramming viết 3 tháng trước
0 0
White
6 2
Bài toán Chào các bạn, hôm nay chúng ta sẽ đả động đến một thứ rất quen tai: caching. Caching là vấn đề muôn thủa và có lẽ là một trong số những v...
Nguyễn Thế Huy viết hơn 2 năm trước
6 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
545 bài viết.
97 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!