Springboot + Vue + WebSocket + Notification实现消息推送功能

实现功能

基于Springboot与Vue架构,首先使用Websocket实现频道订阅,在实现点对点与群发功能后,在前端调用windows自带的消息通知,实现推送功能。

开发环境

  • Springboot 2.6.7
  • vue 2.6.11
  • socket-client 1.0.0

准备工作

在 Vue.js 项目中安装sockjs-client和stompjs。

java 复制代码
npm install sockjs-client stompjs

在后端项目中添加依赖。

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

WebSocket

为完成单播或者广播的频道的订阅功能,需要在前端和后端完成WebSocket的基本配置。

前端配置

需在vue项目中新建websocket.js文件,主要完成:

1.获取userid

2.定义WebSocketService,完成connect、subscribe、unsubscribe、sendMessage、disconnect等主要函数。

代码如下所示。

javascript 复制代码
websocket.js
import SockJS from 'sockjs-client';
import Stomp from 'stompjs';

const userid = JSON.parse(sessionStorage.getItem('CurUser')).id

class WebSocketService {
    constructor() {
        this.stompClient = null;
        this.subscriptions = new Map(); // 存储订阅的频道
    }

    // 连接 WebSocket
    connect() {
        const socket = new SockJS("/api/broadcast"); // 后端地址
        this.stompClient = Stomp.over(socket);
        this.stompClient.connect({}, () => {
            console.log('do connect method');
            console.log('stompClient', this.stompClient)
            this.subscribe('/user/' + userid.toString() + '/alone/getResponse', (response) => {
                if (this.onGlobalNotification) {
                    console.log("message print", response)
                    // this.onGlobalNotification(JSON.parse(message.body));
                    this.onGlobalNotification(response.body);
                }
            });
        });
    }

    // 订阅频道
    subscribe(destination, callback) {
        const subscription = this.stompClient.subscribe(destination, callback);
        this.subscriptions.set(destination, subscription);
    }

    // 取消订阅
    unsubscribe(destination) {
        const subscription = this.subscriptions.get(destination);
        if (subscription) {
            subscription.unsubscribe();
            this.subscriptions.delete(destination);
        }
    }

    // 发送消息到后端
    sendMessage(destination, message) {
        console.log("sendMessage method , message is ", message.content)
        this.stompClient.send(destination, {}, message.content);
    }

    // 断开连接
    disconnect() {
        if (this.stompClient) {
            this.stompClient.disconnect();
            console.log('WebSocket 断开连接!');
        }
    }
}

export default new WebSocketService();

后端配置

在后端同样需要完成频道的订阅,新建文件WebSocketConfig.java,代码如下所示。

java 复制代码
package com.wms.common;

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;

import javax.annotation.Resource;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Resource
    public AppConfig appConfig;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker(
                "/user", "/topic1", "/topic2", "/mass"
        );
        // 点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是 /user/
        // 注意,这里必须和上面设置的Broker:/user 一致(两个都可以自定义,但必须一致)。否则连接不上
        registry.setUserDestinationPrefix("/user/");
        // 指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册一个STOMP的endpoint端点,并指定使用SockJS协议
        // 前端使用这个地址连接后端 WebSocket接口
        registry.addEndpoint("/broadcast", "/point")
                // 允许所有源跨域。还可以指定ip配置:http://ip:*
                // 低版本的SpringBoot(2.1.5.RELEASE 就不行)不行
                .setAllowedOriginPatterns(appConfig.getFrontHttpUrl()) // 此处填写前端页面地址
                .withSockJS();
    }
}

Vue

在用户登录之后,首先初始化WebSocket 连接,然后定义全局通知的回调,在回调函数onGlobalNotification中实现调用windows自带的通知功能Notification。

javascript 复制代码
// 初始化 WebSocket 连接
WebSocketService.connect();
// 定义全局通知的回调
WebSocketService.onGlobalNotification = (message) => {
    this.sendNotification('xxx',message)
    // this.$bus.$emit("postTrigger")
};
javascript 复制代码
    // 发送通知的方法
    sendNotification (title, body) {
      // console.log("sendNotification", Notification.permission)
      // 检查浏览器是否支持 Notification API
      if ('Notification' in window) {
        // 如果通知权限已经授予
        if (Notification.permission === "granted") {
          new Notification(title, {
            body: body,
            icon: logoIcon,
            requireInteraction: true
          });
        } else if (Notification.permission !== "denied") {
          // 请求用户授权
          Notification.requestPermission().then(permission => {
            if (permission === "granted") {
              new Notification(title, {
                body: body,
                icon: logoIcon,
                requireInteraction: true
              });
            }
          });
        }
      } else {
        console.log("浏览器不支持通知功能。");
      }
    },

在需要发送消息的地方使用函数sendMessage,在MethodApi处填写后端对应接口。

javascript 复制代码
WebSocketService.sendMessage(
    '/MethodApi', 
        {content: "message you want send"});

Springboot

在后端需要在controller文件中实现对应的MethodApi接口函数, 确定消息传递的目标用户与对应的消息,调用messagingTemplate中的函数convertAndSendToUser,完成在频道/user/userid/alone/getResponse的消息通知。

java 复制代码
controller.java
@Autowired
private SimpMessagingTemplate messagingTemplate;

// p2p notification
@MessageMapping("/MethodApi")
public void p2pNotify(String content){
	System.out.println(content);
	// 可在此处通过接受到的消息确定目标用户 userid(int) 与 对应的消息 message(string)
	System.out.println("=====发送通知=====");
	messagingTemplate.convertAndSendToUser(userid.toString(),"/alone/getResponse",message);
}

效果图

此时就可以在本地进行测试了,效果如下:

服务器部署

因为windows系统通知需要浏览器给予网页权限,所以需要vue启用https协议,配置如下:

javascript 复制代码
module.exports = {
    devServer: {
        port:8001,
        https:true,
        proxy: {
            '/api': {
                target: "http://localhost:8091/", // 代理目标的基础路径
                secure: true,  // 如果是https接口,需要配置这个参数
                changeOrigin: true, // 支持跨域
                pathRewrite: {
                    '^/api': '',
                }
            }
        }
    },
}
相关推荐
慈云数据2 小时前
从开发到上线:基于 Linux 云服务器的前后端分离项目部署实践(Vue + Node.js)
linux·服务器·vue.js
Mryan20057 小时前
解决GraalVM Native Maven Plugin错误:JAVA_HOME未指向GraalVM Distribution
java·开发语言·spring boot·maven
VX_CXsjNo17 小时前
免费送源码:Java+SSM+Android Studio 基于Android Studio游戏搜索app的设计与实现 计算机毕业设计原创定制
java·spring boot·spring·游戏·eclipse·android studio·android-studio
小小鸭程序员8 小时前
Vue组件化开发深度解析:Element UI与Ant Design Vue对比实践
java·vue.js·spring·ui·elementui
拉不动的猪8 小时前
vue自定义指令的几个注意点
前端·javascript·vue.js
陌路物是人非8 小时前
SpringBoot + Netty + Vue + WebSocket实现在线聊天
vue.js·spring boot·websocket·netty
拉不动的猪9 小时前
uniapp与React Native/vue 的简单对比
前端·vue.js·面试
穿林鸟10 小时前
Spring Boot项目信创国产化适配指南
java·spring boot·后端
伏游10 小时前
【BUG】生产环境死锁问题定位排查解决全过程
服务器·数据库·spring boot·后端·postgresql·bug
爱的叹息11 小时前
SpringBoot集成Redis 灵活使用 TypedTuple 和 DefaultTypedTuple 实现 Redis ZSet 的复杂操作
spring boot·redis·bootstrap