如何在 Asp.Net.Core Mvc Signalr 中制作一对一的聊天系统?
How can I make one to one chat system in Asp.Net.Core Mvc Signalr?
我想用Mssql数据库实现私聊系统。当我发送消息时,此代码用作 public,消息出现在所有客户端。但是我想要一对一的聊天系统。一位用户输入存储在数据库中的接收者 ID 和消息文本,然后将消息发送给 Receiver 。然后该消息出现在具有该接收者id的接收者消息区域。
这是我的js代码
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + ":" + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
这是我的中心 class
using MentorShip.Models;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MentorShip.Hubs
{
public class SignalRChat:Hub
{
Context c = new Context();
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage",user,message);
}
}
}
这是我的 html 代码
<div class="container">
<div class="row"> </div>
<div class="row">
<div id="connectionId"></div>
<div class="col-2">Receiver Id</div>
<div class="col-4"><input type="text" id="userInput" /></div>
</div>
<div class="row">
<div class="col-2">Message</div>
<div class="col-4"><input type="text" id="messageInput" /></div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-6">
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
要将消息发送给特定用户,您可以使用以下方法:
使用单用户组.
您可以为每个用户创建一个群组,然后在您只想联系该用户时向该群组发送消息。每个组的名称是用户的名称。如果一个用户有多个连接,则每个连接 ID 都会添加到该用户的组中。
例如,在getting start document的基础上,我创建了一个SignalR应用程序,它会向所有用户发送消息。然后,在 ChatHub class 中,添加 Authorize 属性并覆盖 OnConnectedAsync() 方法,在 OnConnectedAsync 方法中,我们可以创建一个基于 Identity User 的组。然后,添加一个 SendMessageToGroup 方法来向组发送消息。
[Authorize]
public class ChatHub : Hub
{
public override Task OnConnectedAsync()
{
Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
return base.OnConnectedAsync();
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string sender, string receiver, string message)
{
return Clients.Group(receiver).SendAsync("ReceiveMessage", sender, message);
}
}
在Index.cshtml页面中,添加Receiver input元素,用于输入接收者名称(与群组名称相同)。
<div class="container">
<div class="row"> </div>
<div class="row">
<div class="col-2">Sender</div>
<div class="col-4"><input type="text" id="senderInput" /></div>
</div>
<div class="row">
<div class="col-2">Receiver</div>
<div class="col-4"><input type="text" id="receiverInput" /></div>
</div>
<div class="row">
<div class="col-2">Message</div>
<div class="col-4"><input type="text" id="messageInput" /></div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-6">
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
</div>
然后,更新 chat.js 文件中的 sendButton 单击事件:
document.getElementById("sendButton").addEventListener("click", function (event) {
var sender = document.getElementById("senderInput").value;
var receiver = document.getElementById("receiverInput").value;
var message = document.getElementById("messageInput").value;
if (receiver !="") {
connection.invoke("SendMessageToGroup", sender, receiver, message).catch(function (err) {
return console.error(err.toString());
});
}
else {
connection.invoke("SendMessage", sender, message).catch(function (err) {
return console.error(err.toString());
});
}
event.preventDefault();
});
之后,我们就可以向特定用户发送消息了,截图如下(如果receiver为null,则向所有用户发送消息,否则向特定用户发送消息):
通过 ConnectionID 发送消息。
从上面的示例代码中,在OnConnectedAsyc方法中,我们可以获取到ConnectId和User Name,然后将它们存储到数据库中。然后,您可以在 ChatHub.cs 中添加 SendMessageToUser 方法。在这个方法中,您可以查询数据库并根据接收者名称找到connectionId,然后使用Clients.Client("connectionId").SendAsync()
方法将消息发送给特定用户。
public Task SendMessageToUser(string sender, string receiver, string message)
{
//based on the receiver name to query the database and get the connection id
return Clients.Client("connectionId").SendAsync("ReceiveMessage", sender, message);
}
这里有一些相关的文章,你可以参考一下:
Use hubs in SignalR for ASP.NET Core
Mapping SignalR Users to Connections
更新:
详细步骤:
打开Visual Studio2019(最新版),创建Asp.netCore Web Application(名为SignalRApp,使用.net core 3.1版本):
选择 MVC 模板并将身份验证更改为“个人用户帐户”。
在程序包管理器控制台工具中使用以下命令。更多详细信息,查看EF Core Migrations.
add-migration InitialCreate
update-database
之后,我们可以运行申请并注册用户。登录截图如下:
添加 SignalR 客户端库
- 在解决方案资源管理器中,右键单击项目,然后select 添加 > 客户端库.
- 在 添加客户端库 对话框中,对于 Provider select unpkg.
- 对于库,输入 @microsoft/signalr@latest.
- Select选择特定文件,展开dist/browser文件夹,然后selectsignalr.js和signalr.min.js.
- 将目标位置设置为 wwwroot/js/signalr/,然后 select 安装。
LibMan 创建一个 wwwroot/js/signalr 文件夹并将 selected 文件复制到其中。
创建 SignalR 集线器。
在项目文件夹中,创建一个 Hubs 文件夹并添加一个 ChatHub.cs 文件,代码如下:
namespace SignalRApp.Hubs
{
//require using Microsoft.AspNetCore.SignalR;
//require using Microsoft.AspNetCore.Authorization;
[Authorize]
public class ChatHub : Hub
{
public override Task OnConnectedAsync()
{
Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
return base.OnConnectedAsync();
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string sender, string receiver, string message)
{
return Clients.Group(receiver).SendAsync("ReceiveMessage", sender, message);
}
}
}
在 Startup.cs 文件中配置 SignalR。你可以检查 this article.
startup.cs文件如下:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
添加 SignalR 客户端代码:
[注意]这一步注意js文件路径。如果js文件加载不成功,客户端代码将无法运行。
在Home controller Index page(Index.cshtml)中,替换如下内容:
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<div class="container">
<div class="row"> </div>
<div class="row">
<div class="col-2">Sender</div>
<div class="col-4"><input type="text" id="senderInput" /></div>
</div>
<div class="row">
<div class="col-2">Receiver</div>
<div class="col-4"><input type="text" id="receiverInput" /></div>
</div>
<div class="row">
<div class="col-2">Message</div>
<div class="col-4"><input type="text" id="messageInput" /></div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-6">
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/chat.js"></script>
在 wwwroot/js 文件夹中,使用以下代码创建一个 chat.js 文件:
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var sender = document.getElementById("senderInput").value;
var receiver = document.getElementById("receiverInput").value;
var message = document.getElementById("messageInput").value;
if (receiver != "") {
connection.invoke("SendMessageToGroup", sender, receiver, message).catch(function (err) {
return console.error(err.toString());
});
}
else {
connection.invoke("SendMessage", sender, message).catch(function (err) {
return console.error(err.toString());
});
}
event.preventDefault();
});
之后我们可以通过组向用户发送消息。像这样:
我想用Mssql数据库实现私聊系统。当我发送消息时,此代码用作 public,消息出现在所有客户端。但是我想要一对一的聊天系统。一位用户输入存储在数据库中的接收者 ID 和消息文本,然后将消息发送给 Receiver 。然后该消息出现在具有该接收者id的接收者消息区域。
这是我的js代码
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + ":" + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
这是我的中心 class
using MentorShip.Models;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MentorShip.Hubs
{
public class SignalRChat:Hub
{
Context c = new Context();
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage",user,message);
}
}
}
这是我的 html 代码
<div class="container">
<div class="row"> </div>
<div class="row">
<div id="connectionId"></div>
<div class="col-2">Receiver Id</div>
<div class="col-4"><input type="text" id="userInput" /></div>
</div>
<div class="row">
<div class="col-2">Message</div>
<div class="col-4"><input type="text" id="messageInput" /></div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-6">
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
要将消息发送给特定用户,您可以使用以下方法:
使用单用户组.
您可以为每个用户创建一个群组,然后在您只想联系该用户时向该群组发送消息。每个组的名称是用户的名称。如果一个用户有多个连接,则每个连接 ID 都会添加到该用户的组中。
例如,在getting start document的基础上,我创建了一个SignalR应用程序,它会向所有用户发送消息。然后,在 ChatHub class 中,添加 Authorize 属性并覆盖 OnConnectedAsync() 方法,在 OnConnectedAsync 方法中,我们可以创建一个基于 Identity User 的组。然后,添加一个 SendMessageToGroup 方法来向组发送消息。
[Authorize] public class ChatHub : Hub { public override Task OnConnectedAsync() { Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); return base.OnConnectedAsync(); } public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } public Task SendMessageToGroup(string sender, string receiver, string message) { return Clients.Group(receiver).SendAsync("ReceiveMessage", sender, message); } }
在Index.cshtml页面中,添加Receiver input元素,用于输入接收者名称(与群组名称相同)。
<div class="container"> <div class="row"> </div> <div class="row"> <div class="col-2">Sender</div> <div class="col-4"><input type="text" id="senderInput" /></div> </div> <div class="row"> <div class="col-2">Receiver</div> <div class="col-4"><input type="text" id="receiverInput" /></div> </div> <div class="row"> <div class="col-2">Message</div> <div class="col-4"><input type="text" id="messageInput" /></div> </div> <div class="row"> </div> <div class="row"> <div class="col-6"> <input type="button" id="sendButton" value="Send Message" /> </div> </div> </div>
然后,更新 chat.js 文件中的 sendButton 单击事件:
document.getElementById("sendButton").addEventListener("click", function (event) { var sender = document.getElementById("senderInput").value; var receiver = document.getElementById("receiverInput").value; var message = document.getElementById("messageInput").value; if (receiver !="") { connection.invoke("SendMessageToGroup", sender, receiver, message).catch(function (err) { return console.error(err.toString()); }); } else { connection.invoke("SendMessage", sender, message).catch(function (err) { return console.error(err.toString()); }); } event.preventDefault(); });
之后,我们就可以向特定用户发送消息了,截图如下(如果receiver为null,则向所有用户发送消息,否则向特定用户发送消息):
通过 ConnectionID 发送消息。
从上面的示例代码中,在OnConnectedAsyc方法中,我们可以获取到ConnectId和User Name,然后将它们存储到数据库中。然后,您可以在 ChatHub.cs 中添加 SendMessageToUser 方法。在这个方法中,您可以查询数据库并根据接收者名称找到connectionId,然后使用
Clients.Client("connectionId").SendAsync()
方法将消息发送给特定用户。public Task SendMessageToUser(string sender, string receiver, string message) { //based on the receiver name to query the database and get the connection id return Clients.Client("connectionId").SendAsync("ReceiveMessage", sender, message); }
这里有一些相关的文章,你可以参考一下:
Use hubs in SignalR for ASP.NET Core
Mapping SignalR Users to Connections
更新:
详细步骤:
打开Visual Studio2019(最新版),创建Asp.netCore Web Application(名为SignalRApp,使用.net core 3.1版本):
选择 MVC 模板并将身份验证更改为“个人用户帐户”。
在程序包管理器控制台工具中使用以下命令。更多详细信息,查看EF Core Migrations.
add-migration InitialCreate
update-database
之后,我们可以运行申请并注册用户。登录截图如下:
添加 SignalR 客户端库
- 在解决方案资源管理器中,右键单击项目,然后select 添加 > 客户端库.
- 在 添加客户端库 对话框中,对于 Provider select unpkg.
- 对于库,输入 @microsoft/signalr@latest.
- Select选择特定文件,展开dist/browser文件夹,然后selectsignalr.js和signalr.min.js.
- 将目标位置设置为 wwwroot/js/signalr/,然后 select 安装。 LibMan 创建一个 wwwroot/js/signalr 文件夹并将 selected 文件复制到其中。
创建 SignalR 集线器。
在项目文件夹中,创建一个 Hubs 文件夹并添加一个 ChatHub.cs 文件,代码如下:
namespace SignalRApp.Hubs { //require using Microsoft.AspNetCore.SignalR; //require using Microsoft.AspNetCore.Authorization; [Authorize] public class ChatHub : Hub { public override Task OnConnectedAsync() { Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); return base.OnConnectedAsync(); } public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } public Task SendMessageToGroup(string sender, string receiver, string message) { return Clients.Group(receiver).SendAsync("ReceiveMessage", sender, message); } } }
在 Startup.cs 文件中配置 SignalR。你可以检查 this article.
startup.cs文件如下:
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); services.AddControllersWithViews(); services.AddRazorPages(); services.AddSignalR(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapRazorPages(); endpoints.MapHub<ChatHub>("/chathub"); }); } }
添加 SignalR 客户端代码:
[注意]这一步注意js文件路径。如果js文件加载不成功,客户端代码将无法运行。
在Home controller Index page(Index.cshtml)中,替换如下内容:
@{ ViewData["Title"] = "Index"; } <h1>Index</h1> <div class="container"> <div class="row"> </div> <div class="row"> <div class="col-2">Sender</div> <div class="col-4"><input type="text" id="senderInput" /></div> </div> <div class="row"> <div class="col-2">Receiver</div> <div class="col-4"><input type="text" id="receiverInput" /></div> </div> <div class="row"> <div class="col-2">Message</div> <div class="col-4"><input type="text" id="messageInput" /></div> </div> <div class="row"> </div> <div class="row"> <div class="col-6"> <input type="button" id="sendButton" value="Send Message" /> </div> </div> </div> <div class="row"> <div class="col-12"> <hr /> </div> </div> <div class="row"> <div class="col-6"> <ul id="messagesList"></ul> </div> </div> <script src="~/js/signalr/dist/browser/signalr.js"></script> <script src="~/js/chat.js"></script>
在 wwwroot/js 文件夹中,使用以下代码创建一个 chat.js 文件:
"use strict"; var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build(); //Disable send button until connection is established document.getElementById("sendButton").disabled = true; connection.on("ReceiveMessage", function (user, message) { var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); var encodedMsg = user + " says " + msg; var li = document.createElement("li"); li.textContent = encodedMsg; document.getElementById("messagesList").appendChild(li); }); connection.start().then(function () { document.getElementById("sendButton").disabled = false; }).catch(function (err) { return console.error(err.toString()); }); document.getElementById("sendButton").addEventListener("click", function (event) { var sender = document.getElementById("senderInput").value; var receiver = document.getElementById("receiverInput").value; var message = document.getElementById("messageInput").value; if (receiver != "") { connection.invoke("SendMessageToGroup", sender, receiver, message).catch(function (err) { return console.error(err.toString()); }); } else { connection.invoke("SendMessage", sender, message).catch(function (err) { return console.error(err.toString()); }); } event.preventDefault(); });
之后我们可以通过组向用户发送消息。像这样: