C# VUE3+SignalR使用

162 阅读2分钟
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    <title></title>
    <link href="~/js/element-plus/index.css" rel="stylesheet" />
    @*   顺序不能错*@
    <script src="~/js/vue/vue.js"></script>
    <script src="~/js/element-plus/index.js"></script>
    <script src="~/js/element-plus/zh-cn.js"></script>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/js/websocket.js"></script>
    <style scoped>
        /* 添加一些样式以适应页面布局 */
        button {
            margin: 5px;
        }

        input {
            margin: 5px;
            padding: 5px;
        }

        ul {
            list-style-type: none;
            padding: 0;
        }

        li {
            margin: 5px 0;
            padding: 5px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
        }
    </style>

</head>
<body>

    <div id="app">
        <div>
            <h1>SignalR Chat</h1>
            <input v-model="user" placeholder="Your name" />
            <input v-model="message" placeholder="Type a message" @@keyup.enter="sendMessage" />
            <button @@click="sendMessage">Send</button>
            <ul>
                <li v-for="(msg, index) in messages" :key="index">
                    <strong>{{ msg.user }}:</strong> {{ msg.message }}
                </li>
            </ul>
        </div>
    </div>
    <script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
    <script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script>
    <script>

        const { createApp, reactive, ref, toRefs, onMounted, nextTick, onUnmounted, getCurrentInstance } = Vue
        const { ElMessageBox, ElMessage, ElLoading, ElNotification } = ElementPlus
        const setup = {
            setup() {
                const user = ref('');
                const message = ref('');
                const messages = reactive([]);

                var connection = new signalR.HubConnectionBuilder().withUrl("/ChatHub").build();

                const startConnection = async () => {
                    try {
                        await connection.start();
                        console.log('SignalR Connected.');
                    } catch (err) {
                        console.error('SignalR Connection Error: ', err);
                        setTimeout(startConnection, 5000); 
                    }
                };

                const sendMessage = async () => {
                    if (user.value && message.value) {
                        await connection.invoke('SendMessage', user.value, message.value);
                        message.value = '';
                    }
                };

                connection.on('ReceiveMessage', (user, message) => {
                    ElNotification({
                        title: 'Success',
                        message: user+ message,
                        type: 'success',
                        position: 'bottom-right',
                        duration: 10500,
                    })
                    messages.push({ user, message });
                });
                onMounted(() => {
                    startConnection();
                });

                onUnmounted(() => {
                    connection.stop();
                });

                return {
                    user,
                    message,
                    messages,
                    sendMessage,
                };
            }
        }
        createApp(setup).use(ElementPlus, { locale: ElementPlusLocaleZhCn }).mount("#app");


    </script>


</body>
</html>
using Microsoft.AspNetCore.SignalR;

namespace Net6Test.Signalr
{
    public class ChatHub : Hub
    {
        /// <summary>
        /// 接收前端消息,并发送给连接的全部用户   这里的方法是给前端调用的
        /// </summary>
        /// <param name="user"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendMessage(string user, string message)
        {
            //TO DO 可以加缓存,存储用户信息和对应的连接Id
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }

        ///// <summary>
        ///// 连接成功
        ///// </summary>
        ///// <returns></returns>
        //public override async Task OnConnectedAsync()
        //{
        //    await Clients.All.SendAsync("ConnectMessage", Context.ConnectionId);
        //}

        /// <summary>
        /// 退出连接
        /// </summary>
        /// <returns></returns>
        //public override async Task OnDisconnectedAsync(Exception? exception)
        //{
        //    //TO DO 可以从缓存,返回对应掉线用户,同时清除改用户缓存
        //    await Clients.All.SendAsync("ConnectMessage", Context.ConnectionId, exception?.Message);
        //}
        private static Dictionary<string, string> dicUsers = new Dictionary<string, string>();

        public override Task OnConnectedAsync()    //登录
        {
            Console.WriteLine($"ID:{Context.ConnectionId} 已连接");   //控制台记录
            var cid = Context.ConnectionId;
            //根据id获取指定客户端
            var client = Clients.Client(cid);

            //向指定用户发送消息
            //client.SendAsync("Self", cid);

            //像所有用户发送消息
            Clients.All.SendAsync("ReceivePublicMessageLogin", $"{cid}加入了聊天室");        //界面显示登录
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception? exception)       //退出的时候
        {
            Console.WriteLine($"ID:{Context.ConnectionId} 已断开");
            var cid = Context.ConnectionId;
            //根据id获取指定客户端
            var client = Clients.Client(cid);

            //向指定用户发送消息
            //client.SendAsync("Self", cid);

            //像所有用户发送消息
            Clients.All.SendAsync("ReceivePublicMessageLogin", $"{cid}离开了聊天室");        //界面显示登录
            return base.OnDisconnectedAsync(exception);
        }

        /// <summary>
        /// 向所有客户端发送消息
        /// </summary>
        /// <param name="user"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendPublicMessage(string user, string message)
        {                                                     //string user,
            await Clients.All.SendAsync("ReceivePublicMessage", user, message);   //ReceiveMessage 提供给客户端使用
        }

        /// <summary>
        /// 用户登录,密码就不判断了
        /// </summary>
        /// <param name="userId"></param>
        public void Login(string userId)     //对应前端的invoke
        {
            if (!dicUsers.ContainsKey(userId))
            {
                dicUsers[userId] = Context.ConnectionId;
            }
            Console.WriteLine($"{userId}登录成功,ConnectionId={Context.ConnectionId}");
            //向所有用户发送当前在线的用户列表
            Clients.All.SendAsync("dicUsers", dicUsers.Keys.ToList());   //对应前端的on
        }

        public void ChatOne(string userId, string toUserId, string msg)     //用户  发送到的用户      发送的消息
        {
            string newMsg = $"{userId}对你说{msg}";//组装后的消息体
            //如果当前用户在线
            if (dicUsers.ContainsKey(toUserId))
            {
                Clients.Client(dicUsers[toUserId]).SendAsync("ChatInfo", newMsg);
            }
            else
            {
                //如果当前用户不在线,正常是保存数据库,等上线时加载,暂时不做处理
            }
        }
    }
}
#region  添加SignalR
builder.Services.AddSignalR();
#endregion
app.UseEndpoints(endpoints =>
{
    //SignalR配置前端接口
    endpoints.MapHub<ChatHub>("/chatHub");
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});