翼度科技»论坛 编程开发 .net 查看内容

ASP.NET Core SignalR 系列(三)- JavaScript 客户端

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
本章将和大家分享 ASP.NET Core SignalR 中的 JavaScript 客户端。ASP.NET Core SignalR JavaScript 客户端库使开发人员能够调用服务器端SignalR中心代码。
本文大部分内容摘自微软官网:https://learn.microsoft.com/zh-cn/aspnet/core/signalr/javascript-client?view=aspnetcore-7.0&tabs=visual-studio
废话不多说,下面我们直接进入本章主题: 
1、安装 SignalR 客户端包

ASP.NET Core 共享框架中包含 SignalR 服务器库。JavaScript 客户端库不会自动包含在项目中。对于此教程,使用库管理器 (LibMan) 从 unpkg 获取客户端库。unpkg 是一个快速的全局内容分发网络,适用于 npm 上的所有内容。
在 Visual Studio “解决方案资源管理器” 中,右键单击项目,然后选择 “添加” => “客户端库”。
在“添加客户端库”对话框中:

  • 为“提供程序”选择“unpkg”。
  • 对于“库”,请输入 @microsoft/signalr@latest。
  • 选择“选择特定文件”,展开“dist/browser”文件夹,然后选择 signalr.js 和 signalr.min.js。
  • 将“目标位置”设置为 wwwroot/js/signalr/。(可根据实际情况进行修改)
  • 选择“安装” 。

LibMan 会创建 wwwroot/js/signalr 文件夹并将所选文件复制到该文件夹下。
2、连接到中心

以下代码创建并启动连接。中心的名称不区分大小写:
  1. //第一个参数:加载依赖模块,可以是require_config中定义的短模块名,也可以是完整的模块路径(去掉.js后缀名,根目录为require_config中设置的baseUrl)
  2. //第二个参数:执行加载完后的回调函数
  3. require(['../common/base', 'jquery', 'signalr'], function (base, $, signalR) {
  4.     let axios = base.axios;
  5.     var vm = new base.vue({
  6.         el: '#app', //挂载点
  7.         mixins: [base.mixin], //混入,类似基类的概念
  8.         data: {
  9.             connectionSignalR: '', //SignalR连接
  10.             user: '',
  11.             message: '',
  12.             msgList: []
  13.         },
  14.         //created钩子函数
  15.         created: function () {
  16.             this.initSignalR(); //初始化SignalR
  17.         },
  18.         //mounted钩子函数
  19.         mounted: function () {
  20.             //console.log('This is index mounted');
  21.         },
  22.         //方法
  23.         methods: {
  24.             //初始化SignalR
  25.             initSignalR: function () {
  26.                 var _this = this;
  27.                 var domain = "http://localhost:5296"; //网站域名
  28.                 //创建连接
  29.                 _this.connectionSignalR = new signalR.HubConnectionBuilder()
  30.                     .withUrl(domain + "/chatHub") //跨域的话必须使用绝对路径
  31.                     .configureLogging(signalR.LogLevel.Error) //配置日志等级
  32.                     //自动重新连接,尝试每次重新连接之前默认分别等待 0、2、10 和 30 秒。尝试四次失败后,它会停止尝试重新连接。
  33.                     .withAutomaticReconnect() //.withAutomaticReconnect([0, 2000, 10000, 30000])
  34.                     .build();
  35.                 /*
  36.                     日志等级:
  37.                     signalR.LogLevel.Error:错误消息。 Error仅记录消息。
  38.                     signalR.LogLevel.Warning:有关潜在错误的警告消息。 日志 Warning 和 Error 消息。
  39.                     signalR.LogLevel.Information:无错误的状态消息。 日志 Information 、 Warning 和 Error 消息。
  40.                     signalR.LogLevel.Trace:跟踪消息。 记录所有内容,包括中心和客户端之间传输的数据。
  41.                 */
  42.                 //监听中心(服务端)发送的消息(服务端调用客户端)(订阅)
  43.                 //ReceiveMessage 是服务端调用客户端的方法名
  44.                 _this.connectionSignalR.on("ReceiveMessage", function (user, message) {
  45.                     _this.msgList.push({
  46.                         user: user,
  47.                         message: message
  48.                     });
  49.                 });
  50.                 //启动连接
  51.                 _this.connectionSignalR.start().then(function () {
  52.                     //启动连接后需要立即执行的逻辑
  53.                     //document.getElementById("sendButton").disabled = false;
  54.                 }).catch(function (err) {
  55.                     return console.error(err.toString());
  56.                 });
  57.             },
  58.             //发送消息
  59.             sendMsg: function () {
  60.                 var _this = this;
  61.                 if (_this.message) {
  62.                     //向中心(服务端)发送消息(客户端调用服务端)
  63.                     //SendMessage 是服务端定义的的方法
  64.                     _this.connectionSignalR.invoke("SendMessage", _this.user, _this.message).catch(function (err) {
  65.                         return console.error(err.toString());
  66.                     });
  67.                     _this.message = ''; //发送完清空消息
  68.                 }
  69.             },
  70.         }
  71.     });
  72. });
复制代码
3、跨域连接(CORS)

通常,浏览器从与请求页面相同的域加载连接。但是,有时需要连接到另一个域。
发出跨域请求时,客户端代码必须使用绝对 URL 而不是相对 URL。对于跨域请求,需要将 .withUrl("/chathub") 修改为 .withUrl("https://{App domain name}/chathub") 。
为了防止恶意站点从另一站点读取敏感数据,默认情况下禁用跨域连接。若要允许跨源请求,请启用 CORS:
  1. using SignalRChat.Hubs;
  2. namespace SignalRChat
  3. {
  4.     public class Program
  5.     {
  6.         public static void Main(string[] args)
  7.         {
  8.             var builder = WebApplication.CreateBuilder(args);
  9.             //服务注册(往容器中添加服务)
  10.             // Add services to the container.
  11.             builder.Services.AddControllersWithViews();
  12.             builder.Services.AddSignalR(); //注册中心所需的 SignalR 服务
  13.             //设置允许跨域
  14.             builder.Services.AddCors(options =>
  15.             {
  16.                 options.AddDefaultPolicy(
  17.                     builder =>
  18.                     {
  19.                         builder.WithOrigins("http://localhost:5296", "http://localhost:8080")
  20.                             .AllowAnyHeader()
  21.                             .WithMethods("GET", "POST")
  22.                             .AllowCredentials();
  23.                     });
  24.             });
  25.             var app = builder.Build();
  26.             //配置Http请求处理管道
  27.             // Configure the HTTP request pipeline.
  28.             if (!app.Environment.IsDevelopment())
  29.             {
  30.                 app.UseExceptionHandler("/Home/Error");
  31.             }
  32.             app.UseStaticFiles();
  33.             app.UseRouting();
  34.             app.UseAuthorization();
  35.             //设置允许跨域
  36.             // UseCors must be called before MapHub. //必须在 MapHub 之前调用 UseCors 方法
  37.             app.UseCors();
  38.             //配置MVC路由
  39.             app.MapControllerRoute(
  40.                 name: "areas",
  41.                 pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
  42.             app.MapControllerRoute(
  43.                 name: "default",
  44.                 pattern: "{controller=Home}/{action=Index}/{id?}");
  45.             //配置SignalR终结点
  46.             app.MapHub<ChatHub>("/chatHub"); //中心
  47.             app.MapHub<StronglyTypedChatHub>("/stronglyTypedChatHub"); //强类型中心
  48.             app.Run();
  49.         }
  50.     }
  51. }
复制代码
必须在 MapHub 之前调用 app.UseCors(); 方法。
4、从客户端调用中心方法

JavaScript 客户端通过 HubConnection 的 invoke 方法调用中心的公共方法。invoke 方法接受:

  • 中心方法的名称。
  • 中心方法中定义的任何参数。
在以下突出显示的代码中,中心上的方法名称为 SendMessage。传递到 invoke 的第二个和第三个参数映射到中心方法的 user 和 message 参数:
  1. try {
  2.     await connection.invoke("SendMessage", user, message);
  3. } catch (err) {
  4.     console.error(err);
  5. }
复制代码
仅在默认模式下使用 Azure SignalR 服务 时,才支持从客户端调用中心方法。
invoke 方法返回 JavaScript Promise。 服务器上的方法返回时,Promise 通过返回值(如果有)进行解析。如果服务器上的方法引发错误,Promise 会被拒绝并显示错误消息。使用 async 和 await 或 Promise 的 then 和 catch 方法来处理这些情况。
JavaScript 客户端还可以通过 HubConnection 的 send 方法调用中心的公共方法。与 invoke 方法不同,send 方法不会等待来自服务器的响应。send 方法返回 JavaScript Promise。当消息发送到服务器后,会解析 Promise。如果发送消息时出错,Promise 会被拒绝并显示错误消息。使用 async 和 await 或 Promise 的 then 和 catch 方法来处理这些情况。
使用 send 不会等到服务器收到消息。因此,无法从服务器返回数据或错误。
5、从中心调用客户端方法

若要接收来自中心的消息,请使用 HubConnection 的 on 方法定义方法:

  • JavaScript 客户端方法的名称。
  • 中心传递给方法的参数。
在下面的示例中,方法名称为 ReceiveMessage。参数名称为 user 和 message:
  1. connection.on("ReceiveMessage", (user, message) => {
  2.     const li = document.createElement("li");
  3.     li.textContent = `${user}: ${message}`;
  4.     document.getElementById("messageList").appendChild(li);
  5. });
复制代码
connection.on 中的上述代码在服务器端代码使用 SendAsync 方法调用它时运行:
  1. using Microsoft.AspNetCore.SignalR;
  2. namespace SignalRChat.Hubs;
  3. public class ChatHub : Hub
  4. {
  5.     public async Task SendMessage(string user, string message)
  6.     {
  7.         await Clients.All.SendAsync("ReceiveMessage", user, message);
  8.     }
  9. }
复制代码
SignalR 通过匹配在 SendAsync 和 connection.on 中定义的方法名称和参数来确定要调用的客户端方法。
客户端的最佳做法是在 on 之后 调用 HubConnection 的 start 方法。这样做可确保在收到任何消息之前注册处理程序。
6、错误处理和日志记录

使用 console.error 在客户端无法连接或发送消息时将错误输出到浏览器的控制台:
  1. try {
  2.     await connection.invoke("SendMessage", user, message);
  3. } catch (err) {
  4.     console.error(err);
  5. }
复制代码
在建立连接时,通过传递记录器和要记录的事件类型来设置客户端日志跟踪。可用的日志级别如下所示:

  • signalR.LogLevel.Error:错误消息。仅记录 Error 消息。
  • signalR.LogLevel.Warning:有关潜在错误的警告消息。记录 Warning 和 Error 消息。
  • signalR.LogLevel.Information:没有错误的状态消息。记录 Information、Warning 和 Error 消息。
  • signalR.LogLevel.Trace:跟踪消息。记录所有内容,包括在中心和客户端之间传输的数据。
使用 HubConnectionBuilder 上的 configureLogging 方法配置日志级别。消息将记录到浏览器控制台:
  1. const connection = new signalR.HubConnectionBuilder()
  2.     .withUrl("/chathub")
  3.     .configureLogging(signalR.LogLevel.Information)
  4.     .build();
复制代码
7、重新连接客户端

1)自动重新连接

默认情况下,它不会自动重新连接。如果需要自动重新连接,则可以将  SignalR 的 JavaScript 客户端配置为使用 HubConnectionBuilder 上的 WithAutomaticReconnect 方法。
  1. const connection = new signalR.HubConnectionBuilder()
  2.     .withUrl("/chathub")
  3.     .withAutomaticReconnect()
  4.     .build();
复制代码
如果不使用任何参数,WithAutomaticReconnect 会将客户端配置为每次尝试重新连接之前分别等待 0、2、10 和 30 秒。尝试四次失败后,会停止尝试重新连接。
配置自定义的重新连接尝试次数,withAutomaticReconnect 会接受一个数字数组,表示在每次开始尝试重新连接之前要等待的延迟(以毫秒为单位):
  1. const connection = new signalR.HubConnectionBuilder()
  2.     .withUrl("/chathub")
  3.     .withAutomaticReconnect([0, 0, 10000])
  4.     .build();
  5.     // .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
复制代码
若要更好地控制自动重新连接尝试的时间和次数,可以自行去微软官网了解。
2)手动重新连接

下面的代码演示典型的手动重新连接方法:

  • 创建函数(在本例中为 start 函数)以启动连接。
  • 在连接的 onclose 事件处理程序中调用 start 函数。
  1. async function start() {
  2.     try {
  3.         await connection.start();
  4.         console.log("SignalR Connected.");
  5.     } catch (err) {
  6.         console.log(err);
  7.         setTimeout(start, 5000);
  8.     }
  9. };
  10. connection.onclose(async () => {
  11.     await start();
  12. });
复制代码
生产实现通常使用指数退避或重试指定的次数。
8、浏览器睡眠选项卡

某些浏览器具有选项卡冻结或休眠功能,用于减少非活动选项卡的计算机资源使用量。 这可能会导致 SignalR 连接关闭,并可能导致不必要的用户体验。
浏览器使用启发法判断选项卡是否应进入睡眠状态,下面的代码示例演示如何使用 Web Lock 将选项卡保持为唤醒状态,并避免意外的连接关闭:
  1. var lockResolver;
  2. if (navigator && navigator.locks && navigator.locks.request) {
  3.     const promise = new Promise((res) => {
  4.         lockResolver = res;
  5.     });
  6.     navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
  7.         return promise;
  8.     });
  9. }
复制代码
对于上述代码示例:

  • Web Lock 是实验性的。条件检查可确认浏览器是否支持 Web Lock。
  • 存储承诺解析程序 lockResolver,以便在选项卡进入睡眠状态时可以释放锁。
  • 关闭连接时,通过调用 lockResolver() 释放锁定。释放锁定时,选项卡可进入睡眠状态。
9、SignalR 信息推送的两种通道

1)客户端触发推送,如下所示:

2)服务端触发推送,如下所示:

至此本文就全部介绍完了,想要了解更多内容可参考微软官网。
 
Demo源码:
  1. 链接:https://pan.baidu.com/s/1U4IadFp-qThj0t3axrtNJA
  2. 提取码:bwb3
复制代码
此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/17539560.html
版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!

来源:https://www.cnblogs.com/xyh9039/archive/2023/07/13/17539560.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具