SignalR《二》接着前篇的继续

SignalR身份验证

在ChatRoomHub加上[Authorize] 这样登录了才能发送消息
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using SignalRStudy.Entity;
using System.Security.Claims;

namespace SignalRStudy.Hubs
{
    [Authorize]
    public class ChatRoomHub : Hub
    {

        private readonly UserManager<MyUser> _userManager;

        public ChatRoomHub(UserManager<MyUser> userManager)
        {
            _userManager = userManager;
        }

        //SignalR的基本使用

        public Task SendPublicMessage(string messgae)
        {
            //获取当前连接id
            var connid = this.Context.ConnectionId;
            var msg = $"{connid}:{DateTime.Now}发送了{messgae}";
            //SendAsync 是对SendCoreAsync的封装 
            //对所有人发送消息 只有一个异步方法可以 不屑async 和 await
            //ReceivePublicMessage 为前端接受消息方法
            var res = Clients.All.SendAsync("ReceivePublicMessage", msg);
            return res;
        }

        public async Task<string> SendPrivateMessage(string destUserName, string message)
        {
            var destUser = await _userManager.FindByNameAsync(destUserName);
            if (destUser == null)
            {
                return "DestUserNotFound";
            }
            string destUserId = destUser.Id.ToString();
            string srcUserName = this.Context.User!.FindFirst(ClaimTypes.Name)!.Value;
            string time = DateTime.Now.ToShortTimeString();
            await this.Clients.User(destUserId).SendAsync("ReceivePrivateMessage",
                srcUserName, time, message);
            return "ok";
        }

    }
}
如下图

image-1660235609899

image-1660235635121

image-1660235658647

前端代码(前端 Vue3 我不是很熟,不多讲了)
<template>
    <fieldset>
        <legend>登录</legend>
        <div>
            用户名:<input type="text" v-model="state.loginData.userName"/>
        </div>
        <div>
            密码:<input type="password"  v-model="state.loginData.password">
        </div>
        <div>
            <input type="button" value="登录" v-on:click="loginClick"/>
        </div>
    </fieldset>
    公屏:<input type="text" v-model="state.userMessage" v-on:keypress="txtMsgOnkeypress" />
    <div>
        私聊给<input type="text" v-model="state.privateMsg.destUserName"/>
        说<input type="text" v-model="state.privateMsg.message"
                v-on:keypress="txtPrivateMsgOnkeypress"/>
    </div>
    <div>
        <ul>
            <li v-for="(msg,index) in state.messages" :key="index">{{msg}}</li>
        </ul>
    </div>
</template>
<script>
    import { reactive, onMounted } from 'vue';
    import * as signalR from '@microsoft/signalr';
    import axios from 'axios';
    let connection;
    export default {name: 'Login',
        setup() {
            const state = reactive({accessToken:"",userMessage: "", messages: [],
                loginData: { userName: "", password: "" },
                privateMsg: { destUserName:"",message:""},
            });
            const startConn = async function () {
                const transport = signalR.HttpTransportType.WebSockets;
                const options = { skipNegotiation: true, transport: transport };
                options.accessTokenFactory = () => state.accessToken;
                connection = new signalR.HubConnectionBuilder()
                    .withUrl('https://localhost:7298/Hubs/ChatRoomHub', options)
                    .withAutomaticReconnect().build();
                try {
                    await connection.start();
                } catch (err) {
                    alert(err);
                    return;
                }
                connection.on('ReceivePublicMessage', msg => {
                    state.messages.push(msg);
                });
                connection.on('ReceivePrivateMessage', (srcUser,time,msg) => {
                    state.messages.push(srcUser+"在"+time+"发来私信:"+msg);
                });
                connection.on('UserAdded', userName => {
                    state.messages.push("系统消息:欢迎" + userName+"加入我们!");
                });
                alert("登陆成功可以聊天了");
            };
            const loginClick = async function () {
                const resp = await axios.post('https://localhost:7298/api/Test/Login',
                    state.loginData);
                state.accessToken = resp.data;
                startConn();
            };
            const txtMsgOnkeypress = async function (e) {
                if (e.keyCode != 13) return;
                try {
                    await connection.invoke("SendPublicMessage", state.userMessage);
                }catch (err) {
                    alert(err);
                    return;
                }
                state.userMessage = "";
            };
            const txtPrivateMsgOnkeypress = async function (e) {
                if (e.keyCode != 13) return;
                const destUserName = state.privateMsg.destUserName;
                const msg = state.privateMsg.message;
                try {
                    const ret = await connection.invoke("SendPrivateMessage", destUserName, msg);
                    if (ret != "ok") { alert(ret);};
                } catch (err) {
                    alert(err);
                    return;
                }
                state.privateMsg.message = "";
            };
            return { state, loginClick, txtMsgOnkeypress, txtPrivateMsgOnkeypress };
        },
    }
</script>
<style scoped>
</style>
总结:

对应的方法名和后端对应,参数都对应即可

外部向SignalR的Hub发送消息

TestController代码

    [HttpPost]
        public async Task<IActionResult> SendMsgToUser(AddNewUserRequest req)
        {
            //查询用户
            var user = await _userManager.FindByNameAsync(req.UserName);
            if (user == null)
            {
                return BadRequest($"查无此人{req.UserName}");
            }

            var pm = await _userManager.FindByNameAsync("PM");

            string time = DateTime.Now.ToShortTimeString();
            string msg = @"PM,我来了";
            //这是给PM发送消息
            await _hubContext.Clients.User(pm.Id.ToString()).SendAsync("ReceivePrivateMessage", req.UserName,time, msg);
            return Ok(req.UserName+"存在");
        }

本来应该是添加一个用户,然后给PM(领导)自动发个消息,手下来新人了。 复制的AddUser方法,不想再添加用户了,所以虽然是Post请求 ,我只是查询了数据库是否有此人,然后给PM发了消息。只是单纯学习演示下功能。

测试结果如下

image-1660235686455

SignalR向部分客户端发消息

上述代码其实已经包含了向部分客户端发消息,不过是以调用接口然后发送,下面的是加上前端实现的类似于私聊的功能。

ChatRoomHub里面的SendPrivateMessage方法已经上面的写了,前端只写了一个方法接受ReceivePrivateMessage,在上述前端代码中,不影响渲染。

测试结果如下

image-1660235714905

SignalR综合案例

我就不贴代码了:使用场景是

英汉词典ECDICT中导入单词到数据库。

下载地址:https://github.com/skywind3000/ECDICT/blob/master/stardict.7z

是一个csv文件,目的就是把这个文件读取出来然后存到数据库里面,数据量比较大,通过SignalR通知前端,完成一个进度条的功能。

核心就是 list< entity >存到数据库里面

当前插入条数/总条数

不要一下全部插入,分批量 100或者1000、10000都可以看自己

前端progress就可以通过百分比来形成进度条。

提示:EF Core 目前还不支持批量处理(里面还是一条一条的 ,学习的话 这样也可以)

如果要批量 可以使用sqlbulk或者使用这个https://github.com/yangzhongke/Zack.EFCore.Batch

本文内容大部分都为杨中科老师《ASP.NET Core技术内幕与项目实战》一书中内容,此文只是做学习记录,如有侵权,联系立马删除。