- vừa được xem lúc

[Spring][Websocket] Simple send notifications

0 0 80

Người đăng: Lim Kimhuor

Theo Viblo Asia

Overview

Trong bài viết sẽ tạo mộ ứng dụng web thực thi gửi tin nhắn sử dụng tính năng Websocket với Spring Framework 5.0. Websocket là kết nối 2 chiều, song công, liên tục giữa máy chủ và trình duyệt. Sau khi Websocket được thiết lập kết nối, kết nối vẫn mở cho đến khi client hoặc máy chủ quyết định đóng kết nối này.

Các case sử dụng có thể khi một ứng dụng liên quan nhiều user liên lạc với nhau, từ server đến client chẳng như notification, chat. Chúng ta sẽ build một ứng dụng đơn giản nhận notification sử dụng STOMP messaging với Spring để tạo tương tác qua web khi có một notification được tạo sẽ push message cho các client đã subscribed.

Coding

Dependencies

Trước tiên cần thêm các dependency vào build.gradle.kts như sau

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "2.5.1" id("io.spring.dependency-management") version "1.0.11.RELEASE" kotlin("jvm") version "1.5.10" kotlin("plugin.spring") version "1.5.10"
} group = "dev.hlk"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11 repositories { mavenCentral()
} dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib") // Swagger OpenAPI implementation("org.springdoc:springdoc-openapi-ui:1.5.9") implementation("org.springdoc:springdoc-openapi-kotlin:1.5.9") // Websocket implementation("org.springframework:spring-websocket:5.3.8") implementation("org.springframework:spring-messaging:5.3.8")
} tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "11" }
}

Sau khi thêm cần phải build lại bằng command ./gradlew build hoặc qua nút reload của IDEA Intellij

WebSocket Configuration

Tạo một file config cho Websocket:

// config/WebSocketConfig.kt
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig : WebSocketMessageBrokerConfigurer { override fun configureMessageBroker(registry: MessageBrokerRegistry) { registry.apply { enableSimpleBroker("/topic") } } override fun registerStompEndpoints(registry: StompEndpointRegistry) { registry.apply { addEndpoint("/notification").withSockJS() } }
}

WebSocketConfig được thêm annotation @Configuration để chỉ rằng nó là một configuration class, @EnableWebSocketMessageBroker bật tính năng xử lý Websocket message bởi một message broker.

Method configureMessageBroker() thực thi method mặc định trong WebSocketMessageBrokerConfigurer để config message broker. Nó bắt đầu gọi enableSimpleBroker() để bật một message broker đơn giản trong bộ nhớ chứa tin nhắn cho client với đích có prefix /topic.

Method registerStompEndpoints() đăng ký Websocket endpoint mở cho SockJS client kết nối qua /notification.

Web Controller

Tạo controller NotificationWebController trong /controller/web/NotificationWebController.kt dành cho view.

@Controller
class NotificationWebController { @GetMapping("/notifications") fun index() = ModelAndView("notifications/index")
}

API Controller

Tạo controller NotificationController trong /controller/api/NotificationController.kt dành cho việc push notification.

@RestController
class NotificationController( private val notificationService: NotificationService
) { @PostMapping("/pushNotification") fun pushNotification(@RequestBody payload: NotificationDao): Boolean { return notificationService.send(payload) }
}

Client

Với server-side đã tạo thì bên client cần có phần nhận notification từ server.

Để có thể dùng views cần phải config thêm trong application.yml

spring: mvc: view: prefix: /views/ suffix: .html

Tạo một index.html trong resources/static/views/notifications/index.html.

<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>Websocket</title> <!-- CSS --> <link rel="stylesheet" href="../../css/app.css"> <link rel="stylesheet" href="../../css/bootstrap/bootstrap.min.css"> <link rel="stylesheet" href="../../css/bootstrap/bootstrap-grid.min.css"> <link rel="stylesheet" href="../../css/bootstrap/bootstrap-reboot.min.css"> <!-- JS --> <script src="../../js/jquery.min.js"></script> <script src="../../js/bootstrap/popper.min.js"></script> <script src="../../js/bootstrap/bootstrap.bundle.min.js"></script> <script src="../../js/bootstrap/bootstrap.min.js"></script> <script src="../../js/sockjs.js"></script> <script src="../../js/stomp.js"></script> <script src="../../js/timeago.full.min.js"></script> <script src="../../js/app.js"></script>
</head>
<body>
<h2>Notifications</h2>
<ul id="notifications"></ul> <div class="toast" id="js-toast" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast-header"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> <path style="fill:#FF8A1E;" d="M174.74,430.93c0,44.878,36.382,81.07,81.26,81.07l22.261-103.331L174.74,430.93z"/> <path style="fill:#FF562B;" d="M256,512c44.878,0,81.26-36.192,81.26-81.07L256,408.669V512L256,512z"/> <polygon style="fill:#FFA418;" points="34.846,364.142 34.846,430.93 256,430.93 278.261,341.881 "/> <path style="fill:#FFBE11;" d="M256,0C176.159,0,111.837,59.674,99.965,136.479c-5.683,36.763-25.84,146.535-25.84,146.535 l204.137,22.261L256,0z"/> <g><path style="fill:#FF8A1E;" d="M437.877,283.016c0,0-20.158-109.774-25.842-146.537C400.162,59.674,335.841,0,256,0v305.277 L437.877,283.016z"/> <polygon style="fill:#FF8A1E;" points="256,341.881 256,430.93 477.154,430.93 477.154,364.142 "/> </g> <path style="fill:#FFD460;" d="M256,283.016H74.123c-23.905,18.925-39.277,48.417-39.277,81.126H256l22.261-40.563L256,283.016z"/> <path style="fill:#FFA418;" d="M437.877,283.016H256v81.126h221.154C477.154,331.433,461.781,301.941,437.877,283.016z"/> <g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g> </svg> <strong class="js-toast-title mr-auto"></strong> <small class="js-toast-timestamp"></small> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div class="toast-body js-toast-content"></div>
</div>
</body>
</html>

Tạo app.js trong resources/static/js/app.js

let stompClient = null; function connect() { let socket = new SockJS("/notification"); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { console.log('Connected: ' + frame); stompClient.subscribe("/topic/notifications", function (notification) { showNotification(JSON.parse(notification.body)); }); }); // Reconnect socket socket.onclose = function (e) { console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason); setTimeout(function() { connect(); }, 1000); }
} function disconnect() { if (stompClient != null) { stompClient.disconnect(); } console.log("Disconnected");
} function showNotification(notification) { let elem = document.createElement("li"); let data = "Title: " + notification.title + " - Timestamp: " + notification.timestamp + " - Content: " + notification.content; elem.appendChild(document.createTextNode(data)); $("#notifications").append(elem); $('#js-toast').on('show.bs.toast', function () { let that = $(this); let header = that.children(".toast-header"); header.children(".js-toast-timestamp").text(timeago.format(Date.parse(notification.timestamp))) header.children(".js-toast-title").text(notification.title); that.children(".js-toast-content").text(notification.content); }) $(".toast").toast("show");
} $(function () { disconnect(); connect(); $(".toast").toast({ delay: 2000 // ms });
}); // Disconnect on close tab
window.onbeforeunload = function () { disconnect();
};
  • Phần chính trong đoạn JS là connect()showNotification()
  • connect() function sử dụng SockJSstomp.js mở kết nối đến /notifications, là endpoint SockJS server chờ các kết nối. Khi kết nối thành công, client sẽ subscribe đến đích /topic/notifications phần mà server sẽ publish. Khi nhận notification DOM sẽ chền vào list và có sử dụng Toasts của bootstrap để làm popup.
  • showNotification() function nhận nội dung message để hiển thị.
  • socket.onclose(function(){}) phụ vụ cho việc kết nối lại với server khi server fail/restart.

Demo

Trong demo này cho thấy các trình duyệt nhận notification sau khi thao tác qua swagger gọi 1 API endpoint /notifications và nội dụng gồm title, content, timestamp.

Cảm ơn các bạn đã đọc bài viết. ?

References

Bình luận

Bài viết tương tự

- vừa được xem lúc

Kotlin dễ ẹc - Lớp vs đối tượng: Object expression và Object declaration

Khi nào dùng. . Khi muốn tạo một đối tượng với những sự thay đổi nhỏ của lớp mà không phải khai báo tường minh lớp con của lớp đó. .

0 0 36

- vừa được xem lúc

Di chuyển, truyền dữ liệu giữa các màn hình trong kotlin android

Di chuyển và Truyền dữ liệu giữa các màn hình trong kotlin android. 1. Di chuyển giữa các màn hình. .

0 0 79

- vừa được xem lúc

Làm quen với Kotlin - Extension

Xin chào các bạn, hôm nay chúng ta sẽ tìm hiểu về một tính năng mới khác của Kotlin có tên là "Extension". Sử dụng extension, chúng ta sẽ có thể thêm hoặc xóa một số method function ngay cả khi không kế thừa hoặc sửa đổi chúng.

0 0 28

- vừa được xem lúc

Sử dụng Hibernate với Kotlin dựa trên Spring Boot

Trong bài viết sẽ mô phỏng các bước để sử dụng hibernate với Kotlin. Hibernate là một framework phụ vụ object-relational-mapping(ORM) trên JVM sử dụng để lưu trữ liên tục các Plain Old Java Object (POJOs) trong quan hệ cơ sở dữ liệu.

0 0 23

- vừa được xem lúc

Top 25 Lib and Projects của nửa đầu năm 2020 — Summer Edition

Năm 2020 đã qua, chúng ta hãy cùng ngồi điểm lại một số repo hay về Android trong năm vừa qua nhé . Và cùng đón 2021 nhiều niềm vui, may mắn hơn nha . 1. Pokedex.

0 0 28

- vừa được xem lúc

Simplifying APIs with coroutines and Flow

Bài viết này trình bày cách đơn giản hóa các API sử dụng coroutines và Flow cũng như cách tạo bộ điều hợp của riêng bạn bằng cách sử dụng các API pauseCancellableCoroutine và callbackFlow. Đối với những người thích đi sâu vào bên trong các quy trình, những API đó sẽ được mổ xẻ và bạn sẽ thấy chúng h

0 0 29