本章將和大家分享 ASP.NET Core SignalR 中的中心(服務(wù)端)。
本文大部分內(nèi)容摘自微軟官網(wǎng):https://learn.microsoft.com/zh-cn/aspnet/core/signalr/hubs?view=aspnetcore-7.0
廢話不多說(shuō),我們直接來(lái)看一個(gè)Demo,Demo的目錄結(jié)構(gòu)如下所示:
本Demo的Web項(xiàng)目為ASP.NET Core Web 應(yīng)用程序(目標(biāo)框架為.NET 7.0) MVC項(xiàng)目。
1、創(chuàng)建和使用中心
通過(guò)聲明繼承自 Hub 的類來(lái)創(chuàng)建中心。將方法添加到 public 類,使其可從客戶端調(diào)用:
using Microsoft.AspNetCore.SignalR; namespace SignalRChat.Hubs { /// <summary> /// Hub 類管理連接、組和消息 /// </summary> public class ChatHub : Hub { /// <summary> /// 可通過(guò)已連接客戶端調(diào)用 SendMessage,以向所有客戶端發(fā)送消息 /// </summary> public async Task SendMessage(string user, string message) { //Clients.All 向所有的客戶端發(fā)送消息(服務(wù)端調(diào)用客戶端) //ReceiveMessage 是客戶端監(jiān)聽(tīng)的方法 await Clients.All.SendAsync("ReceiveMessage", user, message); /* // 常用方法 // 給所有人發(fā)送消息 await Clients.All.SendAsync("ReceiveMessage", data); // 給組里所有人發(fā)消息 await Clients.Group("Users").SendAsync("ReceiveMessage", data); // 給調(diào)用方法的那個(gè)人發(fā)消息 await Clients.Caller.SendAsync("ReceiveMessage", data); // 給除了調(diào)用方法的以外所有人發(fā)消息 await Clients.Others.SendAsync("ReceiveMessage", data); // 給指定connectionId的人發(fā)消息 await Clients.User(connectionId).SendAsync("ReceiveMessage", data); // 給指定connectionId的人發(fā)消息 await Clients.Client(connectionId).SendAsync("ReceiveMessage", data); // 給指定connectionId的人發(fā)消息,同時(shí)指定多個(gè)connectionId await Clients.Clients(IReadOnlyList<> connectionIds).SendAsync("ReceiveMessage", data); */ } } }
中心是暫時(shí)性的:
不要將狀態(tài)存儲(chǔ)在中心類的屬性中。每個(gè)中心方法調(diào)用都在新的中心實(shí)例上執(zhí)行。
請(qǐng)勿通過(guò)依賴項(xiàng)注入直接實(shí)例化中心。若要從應(yīng)用程序中的其他位置向客戶端發(fā)送消息,請(qǐng)使用 IHubContext 。
調(diào)用依賴于保持活動(dòng)狀態(tài)的中心的異步方法時(shí)請(qǐng)使用 await。例如,如果在沒(méi)有 await 的情況下進(jìn)行調(diào)用,則 Clients.All.SendAsync(...) 這類方法會(huì)失敗,并且中心方法會(huì)在 SendAsync 完成之前完成。
2、配置 SignalR 中心?
注冊(cè)中心所需的 SignalR 服務(wù) 以及 配置SignalR終結(jié)點(diǎn),修改 Program.cs 文件的代碼,如下所示:
using SignalRChat.Hubs; namespace SignalRChat { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); //服務(wù)注冊(cè)(往容器中添加服務(wù)) // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddSignalR(); //注冊(cè)中心所需的 SignalR 服務(wù) var app = builder.Build(); //配置Http請(qǐng)求處理管道 // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); //配置MVC路由 app.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); //配置SignalR終結(jié)點(diǎn) app.MapHub<ChatHub>("/chatHub"); //中心 app.MapHub<StronglyTypedChatHub>("/stronglyTypedChatHub"); //強(qiáng)類型中心 app.Run(); } } }
3、上下文對(duì)象
類 Hub 包含一個(gè) Context 屬性,該屬性包含以下屬性以及有關(guān)連接的信息:
屬性 | 說(shuō)明 |
ConnectionId | 獲取連接的唯一 ID(由 SignalR 分配)。每個(gè)連接都有一個(gè)連接 ID。 |
UserIdentifier | 獲取用戶標(biāo)識(shí)符。 默認(rèn)情況下,SignalR 使用與連接關(guān)聯(lián)的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作為用戶標(biāo)識(shí)符。 |
User | 獲取與當(dāng)前用戶關(guān)聯(lián)的 ClaimsPrincipal。 |
Items | 獲取可用于在此連接范圍內(nèi)共享數(shù)據(jù)的鍵/值集合。數(shù)據(jù)可以存儲(chǔ)在此集合中,會(huì)在不同的中心方法調(diào)用間為連接持久保存。 |
Features | 獲取連接上可用的功能的集合。目前,在大多數(shù)情況下不需要此集合,因此未對(duì)其進(jìn)行詳細(xì)記錄。 |
ConnectionAborted | 獲取一個(gè) CancellationToken,它會(huì)在連接中止時(shí)發(fā)出通知。 |
Hub.Context 還包含以下方法:
方法 | 說(shuō)明 |
GetHttpContext | 獲取 Http 請(qǐng)求的上下文對(duì)象,如果不是 Http 請(qǐng)求,則返回 null 。 |
Abort | 中止連接。 |
4、客戶端對(duì)象
類 Hub 包含一個(gè) Clients 屬性,該屬性包含以下屬性,用于服務(wù)器和客戶端之間的通信:
屬性 | 說(shuō)明 |
?All | ?對(duì)所有連接的客戶端調(diào)用方法 |
?Caller | ?對(duì)調(diào)用了中心方法的客戶端調(diào)用方法 |
?Others | ?對(duì)所有連接的客戶端調(diào)用方法(調(diào)用了方法的客戶端除外) |
Hub.Clients 還包含以下方法:
方法 | 說(shuō)明 |
AllExcept | 對(duì)所有連接的客戶端調(diào)用方法(指定連接除外) |
Client | 對(duì)連接的一個(gè)特定客戶端調(diào)用方法 |
Clients | 對(duì)連接的多個(gè)特定客戶端調(diào)用方法 |
Group | 對(duì)指定組中的所有連接調(diào)用方法 |
GroupExcept | 對(duì)指定組中的所有連接調(diào)用方法(指定連接除外) |
Groups | 對(duì)多個(gè)連接組調(diào)用方法 |
OthersInGroup | 對(duì)一個(gè)連接組調(diào)用方法(不包括調(diào)用了中心方法的客戶端) |
User | 對(duì)與一個(gè)特定用戶關(guān)聯(lián)的所有連接調(diào)用方法 |
Users | 對(duì)與多個(gè)指定用戶關(guān)聯(lián)的所有連接調(diào)用方法 |
以上表中的每個(gè)屬性或方法都返回具有 SendAsync 方法的對(duì)象。 方法 SendAsync 接收要調(diào)用的客戶端方法的名稱和任何參數(shù)。
5、向客戶端發(fā)送消息
若要對(duì)特定客戶端發(fā)出調(diào)用,請(qǐng)使用 Clients 對(duì)象的屬性。?在以下示例中,有三種中心方法:
using Microsoft.AspNetCore.SignalR; namespace SignalRChat.Hubs { /// <summary> /// Hub 類管理連接、組和消息 /// </summary> public class ChatHub : Hub { /// <summary> /// 可通過(guò)已連接客戶端調(diào)用 SendMessage,以向所有客戶端發(fā)送消息 /// </summary> public async Task SendMessage(string user, string message) { //Clients.All 向所有的客戶端發(fā)送消息(服務(wù)端調(diào)用客戶端) //ReceiveMessage 是客戶端監(jiān)聽(tīng)的方法 await Clients.All.SendAsync("ReceiveMessage", user, message); //將消息發(fā)送到所有連接的客戶端 /* // 常用方法 // 給所有人發(fā)送消息 await Clients.All.SendAsync("ReceiveMessage", data); // 給組里所有人發(fā)消息 await Clients.Group("Users").SendAsync("ReceiveMessage", data); // 給調(diào)用方法的那個(gè)人發(fā)消息 await Clients.Caller.SendAsync("ReceiveMessage", data); // 給除了調(diào)用方法的以外所有人發(fā)消息 await Clients.Others.SendAsync("ReceiveMessage", data); // 給指定connectionId的人發(fā)消息 await Clients.User(connectionId).SendAsync("ReceiveMessage", data); // 給指定connectionId的人發(fā)消息 await Clients.Client(connectionId).SendAsync("ReceiveMessage", data); // 給指定connectionId的人發(fā)消息,同時(shí)指定多個(gè)connectionId await Clients.Clients(IReadOnlyList<> connectionIds).SendAsync("ReceiveMessage", data); */ } public async Task SendMessageToCaller(string user, string message) => await Clients.Caller.SendAsync("ReceiveMessage", user, message); //將消息發(fā)送回調(diào)用方 public async Task SendMessageToGroup(string user, string message) => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message); //將消息發(fā)送給 SignalR Users 組中的所有客戶端 } }
6、強(qiáng)類型中心
使用 SendAsync 的缺點(diǎn)是它依賴于字符串來(lái)指定要調(diào)用的客戶端方法。 如果客戶端中的方法名稱拼寫(xiě)錯(cuò)誤或缺失,則這會(huì)使代碼可能出現(xiàn)運(yùn)行時(shí)錯(cuò)誤。
使用 SendAsync 的另一種方法是使用 Hub<T>強(qiáng)類型Hub類。 在以下示例中 ChatHub ,客戶端方法已提取到名為 的 IChatClient接口中:
namespace SignalRChat.Hubs { public interface IChatClient { /// <summary> /// 客戶端方法 /// </summary> Task ReceiveMessage(string user, string message); } }
此接口可用于將前面的 ChatHub 示例重構(gòu)為強(qiáng)類型:
using Microsoft.AspNetCore.SignalR; namespace SignalRChat.Hubs { /// <summary> /// 強(qiáng)類型中心 /// 中心是暫時(shí)性的:不要將狀態(tài)存儲(chǔ)在中心類的屬性中。每個(gè)中心方法調(diào)用都在新的中心實(shí)例上執(zhí)行。 /// </summary> public class StronglyTypedChatHub : Hub<IChatClient> { [HubMethodName("SendMessage")] //更改中心方法的名稱 public async Task SendMessage(string user, string message) => await Clients.All.ReceiveMessage(user, message); //將消息發(fā)送到所有連接的客戶端 public async Task SendMessageToCaller(string user, string message) => await Clients.Caller.ReceiveMessage(user, message); //將消息發(fā)送回調(diào)用方 public async Task SendMessageToGroup(string user, string message) => await Clients.Group("SignalR Users").ReceiveMessage(user, message); //將消息發(fā)送給 SignalR Users 組中的所有客戶端 } }
使用 Hub<IChatClient> 可以對(duì)客戶端方法進(jìn)行編譯時(shí)檢查。 這可以防止使用字符串導(dǎo)致的問(wèn)題,因?yàn)?Hub<T> 只能提供對(duì) 接口中定義的方法的訪問(wèn)。 使用強(qiáng)類型 Hub<T> 會(huì)禁止使用 SendAsync。
備注:Async后綴不會(huì)從方法名稱中去除。 除非使用 .on('MyMethodAsync')定義客戶端方法,否則不要使用 MyMethodAsync 作為名稱。
7、更改中心方法的名稱
默認(rèn)情況下,服務(wù)器中心方法名稱是 .NET 方法的名稱。若要更改特定方法的此默認(rèn)行為,請(qǐng)使用 HubMethodName 特性。調(diào)用方法時(shí),客戶端應(yīng)使用此名稱而不是 .NET 方法名稱:
[HubMethodName("SendMessageToUser")] public async Task DirectMessage(string user, string message) => await Clients.User(user).SendAsync("ReceiveMessage", user, message);
8、將服務(wù)注入中心
中心構(gòu)造函數(shù)可以接受 DI 中的服務(wù)作為參數(shù),這些參數(shù)可以存儲(chǔ)在類的屬性中,以便在中心方法中使用。
為不同的中心方法注入多個(gè)服務(wù)或作為編寫(xiě)代碼的替代方法時(shí),中心方法也可以接受 DI 中的服務(wù)。 默認(rèn)情況下,如果可能,將從 DI 檢查和解析中心方法參數(shù)。
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>(); // ... public class ChatHub : Hub { public Task SendMessage(string user, string message, IDatabaseService dbService) { var userName = dbService.GetUserName(user); return Clients.All.SendAsync("ReceiveMessage", userName, message); } }
如果不需要從服務(wù)隱式解析參數(shù),請(qǐng)使用 DisableImplicitFromServicesParameters 禁用它。 若要在中心方法中顯式指定從 DI 解析的參數(shù),請(qǐng)使用 DisableImplicitFromServicesParameters 選項(xiàng),并使用 [FromServices] 屬性或自定義屬性,該屬性在應(yīng)從 DI 解析的中心方法參數(shù)上實(shí)現(xiàn) IFromServiceMetadata 。
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>(); services.AddSignalR(options => { options.DisableImplicitFromServicesParameters = true; }); // ... public class ChatHub : Hub { public Task SendMessage(string user, string message, [FromServices] IDatabaseService dbService) { var userName = dbService.GetUserName(user); return Clients.All.SendAsync("ReceiveMessage", userName, message); } }
9、為連接處理事件
SignalR 中心 API 提供 OnConnectedAsync 和 OnDisconnectedAsync 虛方法來(lái)管理和跟蹤連接。
/// <summary> /// 在客戶端連接到中心時(shí)執(zhí)行操作 /// </summary> /// <returns></returns> public override async Task OnConnectedAsync() { await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnConnectedAsync(); } /// <summary> /// 在客戶端斷開(kāi)連接時(shí)執(zhí)行操作 /// </summary> /// <param name="exception"></param> /// <returns></returns> public override async Task OnDisconnectedAsync(Exception? exception) { await base.OnDisconnectedAsync(exception); }
其中如果客戶端有意斷開(kāi)連接(例如通過(guò)調(diào)用 connection.stop()),則 exception 參數(shù)為 null。但是,如果客戶端由于錯(cuò)誤(例如網(wǎng)絡(luò)故障)而斷開(kāi)連接,則 exception 參數(shù)包含描述故障的異常。
RemoveFromGroupAsync 無(wú)需在 OnDisconnectedAsync 中調(diào)用,系統(tǒng)會(huì)自動(dòng)處理它。
10、從中心外部發(fā)送消息
SignalR 中心是用于向連接到 SignalR 服務(wù)器的客戶端發(fā)送消息的核心抽象。 你也可以使用 IHubContext 服務(wù)從應(yīng)用中的其他位置發(fā)送消息。
備注:IHubContext 用于將通知發(fā)送到客戶端,而非用于調(diào)用 Hub 上的方法。
1)獲取 IHubContext 實(shí)例
在 ASP.NET Core SignalR 中,你可以通過(guò)依賴項(xiàng)注入來(lái)訪問(wèn) IHubContext 實(shí)例。 你可以將 IHubContext 實(shí)例注入控制器、中間件或其他 DI 服務(wù)。 使用該實(shí)例向客戶端發(fā)送消息。
2)在控制器中注入 IHubContext 實(shí)例
通過(guò)將 IHubContext 實(shí)例添加到構(gòu)造函數(shù),可以將其注入控制器:
public class HomeController : Controller { private readonly IHubContext<NotificationHub> _hubContext; public HomeController(IHubContext<NotificationHub> hubContext) { _hubContext = hubContext; } }
獲權(quán)訪問(wèn) IHubContext 實(shí)例后,就像在中心本身一樣調(diào)用客戶端方法:
public async Task<IActionResult> Index() { await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}"); return View(); }
3)在中間件中獲取 IHubContext 實(shí)例
訪問(wèn)中間件管道中的 IHubContext,如下所示:
app.Use(async (context, next) => { var hubContext = context.RequestServices .GetRequiredService<IHubContext<ChatHub>>(); //... if (next != null) { await next.Invoke(); } });
備注:當(dāng)從 Hub 類外部調(diào)用客戶端方法時(shí),沒(méi)有與該調(diào)用關(guān)聯(lián)的調(diào)用方。 因此,無(wú)法訪問(wèn) ConnectionId、Caller 和 Others 屬性。
4)從 IHost 獲取 IHubContext 實(shí)例
從 Web 主機(jī)訪問(wèn) IHubContext 對(duì)于與 ASP.NET Core 之外的區(qū)域集成很有用,例如,使用第三方依賴項(xiàng)注入框架:
public class Program { public static void Main(string[] args) { var host = CreateHostBuilder(args).Build(); var hubContext = host.Services.GetService(typeof(IHubContext<ChatHub>)); host.Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
5)注入強(qiáng)類型 HubContext
若要注入強(qiáng)類型 HubContext,請(qǐng)確保中心繼承自 Hub<T>。 使用 IHubContext<THub, T> 接口而不是 IHubContext<THub> 進(jìn)行注入。
public class ChatController : Controller { public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; } public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext) { _strongChatHubContext = chatHubContext; } public async Task SendMessage(string user, string message) { await _strongChatHubContext.Clients.All.ReceiveMessage(user, message); } }
6)在泛型代碼中使用 IHubContext
注入的 IHubContext<THub> 實(shí)例可以強(qiáng)制轉(zhuǎn)換為 IHubContext,而無(wú)需指定泛型 Hub 類型。
class MyHub : Hub { } class MyOtherHub : Hub { } app.Use(async (context, next) => { var myHubContext = context.RequestServices .GetRequiredService<IHubContext<MyHub>>(); var myOtherHubContext = context.RequestServices .GetRequiredService<IHubContext<MyOtherHub>>(); await CommonHubContextMethod((IHubContext)myHubContext); await CommonHubContextMethod((IHubContext)myOtherHubContext); await next.Invoke(); } async Task CommonHubContextMethod(IHubContext context) { await context.Clients.All.SendAsync("clientMethod", new Args()); }
此操作在以下情況下十分有用:
- 編寫(xiě)不引用應(yīng)用正在使用的特定 Hub 類型的庫(kù)。
- 編寫(xiě)可應(yīng)用于多個(gè)不同 Hub 實(shí)現(xiàn)的泛型代碼。
11、管理 SignalR 中的用戶和組
SignalR 允許將消息發(fā)送到與特定用戶關(guān)聯(lián)的所有連接,以及指定的連接組。
1)SignalR 中的用戶
SignalR 中的單個(gè)用戶可以與一個(gè)應(yīng)用建立多個(gè)連接。 例如,用戶可以在桌面和手機(jī)上進(jìn)行連接。 每臺(tái)設(shè)備都有一個(gè)單獨(dú)的 SignalR 連接,但它們都與同一個(gè)用戶關(guān)聯(lián)。 如果向用戶發(fā)送消息,則與該用戶關(guān)聯(lián)的所有連接都會(huì)收到消息。 可以通過(guò)中心內(nèi)的 Context.UserIdentifier 屬性訪問(wèn)連接的用戶標(biāo)識(shí)符。
默認(rèn)情況下,SignalR 使用與連接關(guān)聯(lián)的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作為用戶標(biāo)識(shí)符。 若要自定義此行為,請(qǐng)參閱使用聲明自定義標(biāo)識(shí)處理。
通過(guò)將用戶標(biāo)識(shí)符傳遞給中心方法中的 User 函數(shù),向特定用戶發(fā)送消息,如以下示例所示:
public Task SendPrivateMessage(string user, string message) { return Clients.User(user).SendAsync("ReceiveMessage", message); }
備注:用戶標(biāo)識(shí)符區(qū)分大小寫(xiě)。
2)SignalR 中的組
組是與名稱關(guān)聯(lián)的連接集合。你可以將消息發(fā)送到組中的所有連接。建議通過(guò)組發(fā)送到一個(gè)或多個(gè)連接,因?yàn)榻M由應(yīng)用程序管理。一個(gè)連接可以是多個(gè)組的成員。組非常適合聊天應(yīng)用程序之類的應(yīng)用,其中每個(gè)聊天室都可以表示為一個(gè)組。可通過(guò) AddToGroupAsync 和 RemoveFromGroupAsync 方法在組中添加或刪除連接。
public async Task AddToGroup(string groupName) { await Groups.AddToGroupAsync(Context.ConnectionId, groupName); await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}."); } public async Task RemoveFromGroup(string groupName) { await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName); await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has left the group {groupName}."); }
重新連接時(shí)不會(huì)保留組成員身份。重新建立連接后,需要重新加入組。無(wú)法計(jì)算組的成員數(shù),因?yàn)槿绻麑?yīng)用程序擴(kuò)展到多臺(tái)服務(wù)器,則無(wú)法獲取此信息。
若要在使用組時(shí)保護(hù)對(duì)資源的訪問(wèn),請(qǐng)使用 ASP.NET Core 中的身份驗(yàn)證和授權(quán)功能。如果僅當(dāng)憑據(jù)對(duì)組有效時(shí)才將用戶添加到該組,則發(fā)送到該組的消息將僅發(fā)送給授權(quán)用戶。但是,組不是一項(xiàng)安全功能。身份驗(yàn)證聲明具有組不具備的功能,例如到期和撤銷。如果撤銷用戶對(duì)組的訪問(wèn)權(quán)限,應(yīng)用必須從組中顯式刪除該用戶。
備注:組名稱區(qū)分大小寫(xiě)。
12、SignalR API 設(shè)計(jì)注意事項(xiàng)
使用自定義對(duì)象參數(shù)確保向后兼容性
將新的參數(shù)添加到 SignalR 客戶端或服務(wù)器上的中心方法是一項(xiàng)重大更改。這意味著,較舊的客戶端/服務(wù)器在嘗試調(diào)用沒(méi)有適當(dāng)數(shù)量參數(shù)的方法時(shí)會(huì)出錯(cuò)。但是,向自定義對(duì)象參數(shù)添加屬性不是一項(xiàng)中斷性變更。這可用于設(shè)計(jì)兼容的 API,以適應(yīng)客戶端或服務(wù)器上的更改。
使用自定義對(duì)象作為參數(shù)可提供更大的靈活性,如下所示:
public class TotalLengthRequest { public string Param1 { get; set; } public string Param2 { get; set; } } public async Task GetTotalLength(TotalLengthRequest req) { var length = req.Param1.Length; if (req.Param2 != null) { length += req.Param2.Length; } return length; }
當(dāng)舊客戶端發(fā)送單個(gè)參數(shù)時(shí),額外的?Param2
?屬性將保留為?null
。 你可以通過(guò)檢查?Param2
?是否為?null
?來(lái)檢測(cè)舊客戶端發(fā)送的消息并應(yīng)用默認(rèn)值。 新客戶端可以發(fā)送這兩個(gè)參數(shù)。
connection.invoke("GetTotalLength", { param1: "value1", param2: "value2" });
此技術(shù)同樣適用于客戶端上定義的方法。 你可以從服務(wù)器端發(fā)送自定義對(duì)象:
public async Task Broadcast(string message) { await Clients.All.SendAsync("ReceiveMessage", new { Sender = Context.User.Identity.Name, Message = message }); }
舊客戶端不需要?Sender
?值,因此會(huì)忽略它。 新客戶端可以通過(guò)更新為讀取新屬性來(lái)接受它:
connection.on("ReceiveMessage", (req) => { let message = req.message; if (req.sender) { message = req.sender + ": " + message; } appendMessageToChatWindow(message); });
在這種情況下,新客戶端也可以容忍不提供?Sender
?值的舊服務(wù)器。由于舊服務(wù)器不提供?Sender
?值,因此客戶端在訪問(wèn)它之前會(huì)檢查它是否存在。
?
Demo源碼:
鏈接:https://pan.baidu.com/s/1AbGaPRfv2vAskHRAnOlvYA 提取碼:456q
此文由博主精心撰寫(xiě)轉(zhuǎn)載請(qǐng)保留此原文鏈接:https://www.cnblogs.com/xyh9039/p/17536693.html文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-533740.html
版權(quán)聲明:如有雷同純屬巧合,如有侵權(quán)請(qǐng)及時(shí)聯(lián)系本人修改,謝謝!??!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-533740.html
到了這里,關(guān)于ASP.NET Core SignalR 系列(二)- 中心(服務(wù)端)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!