- ASP.NET Core SignalR JavaScript 客户端ASP.NET Core SignalR JavaScript client
- 安装 SignalR 客户端包Install the SignalR client package
- 使用 SignalR JavaScript 客户端Use the SignalR JavaScript client
- 连接到中心Connect to a hub
- 从客户端调用集线器方法Call hub methods from client
- 从集线器调用客户端方法Call client methods from hub
- 错误处理和日志记录Error handling and logging
- 重新连接客户端Reconnect clients
- 其他资源Additional resources
ASP.NET Core SignalR JavaScript 客户端ASP.NET Core SignalR JavaScript client
本文内容
作者:Rachel Appel
ASP.NET Core SignalR JavaScript 客户端库使开发人员能够调用服务器端集线器代码。
安装 SignalR 客户端包Install the SignalR client package
SignalR JavaScript 客户端库作为npm包提供。如果使用的是 Visual Studio,请在根文件夹中的 "包管理器控制台" 中运行 npm install
。对于 Visual Studio Code,请从集成终端运行命令。
npm init -y
npm install @microsoft/signalr
npm 将包内容安装node_modules\@microsoft\signalr\dist\browser 文件夹中。在wwwroot\lib文件夹下,创建名为signalr的新文件夹。将signalr文件复制到wwwroot\lib\signalr文件夹。
npm init -y
npm install @aspnet/signalr
npm 将包内容安装node_modules\@aspnet\signalr\dist\browser 文件夹中。在wwwroot\lib文件夹下,创建名为signalr的新文件夹。将signalr文件复制到wwwroot\lib\signalr文件夹。
使用 SignalR JavaScript 客户端Use the SignalR JavaScript client
引用 <script>
元素中的 SignalR JavaScript 客户端。
<script src="~/lib/signalr/signalr.js"></script>
连接到中心Connect to a hub
下面的代码创建并启动连接。在中心的名称是不区分大小写。
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.configureLogging(signalR.LogLevel.Information)
.build();
start();
/* this is here to show an alternative to start, with a then
跨域的连接Cross-origin connections
通常情况下,浏览器请求的页面所在的域从加载的连接。但是,有一些情况下需要与另一个域的连接时。
为了防止恶意站点读取其他站点中的敏感数据,默认情况下会禁用跨域连接。若要允许跨源请求,请在 Startup
类中启用它。
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SignalRChat.Hubs;
namespace SignalRChat
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc();
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("http://localhost:55830")
.AllowCredentials();
}));
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chathub");
});
app.UseMvc();
}
}
}
从客户端调用集线器方法Call hub methods from client
JavaScript 客户端通过HubConnection的invoke方法在集线器上调用公共方法。invoke
方法接受两个参数:
集线器方法的名称。在下面的示例中,集线器上的方法名称是
SendMessage
。在集线器方法中定义的任何参数。在下面的示例中,参数名称为
message
。示例代码使用了在所有主要浏览器(Internet Explorer 除外)的当前版本中受支持的箭头函数语法。
connection.invoke("SendMessage", user, message).catch(err => console.error(err.toString()));
备注
如果在无服务器模式下使用 Azure SignalR 服务,则无法从客户端调用集线器方法。有关详细信息,请参阅SignalR 服务文档。
invoke
方法返回 JavaScript承诺。当服务器上的方法返回时,将用返回值(如果有)解析 Promise
。如果服务器上的方法引发错误,则会拒绝 Promise
错误消息。使用 Promise
本身的 then
和 catch
方法来处理这些情况(或 await
语法)。
send
方法返回 JavaScript Promise
。当消息发送到服务器时,将解析 Promise
。如果发送消息时出错,则会拒绝 Promise
,并会出现错误消息。使用 Promise
本身的 then
和 catch
方法来处理这些情况(或 await
语法)。
备注
使用 send
不会等到服务器收到消息。因此,不可能从服务器返回数据或错误。
从集线器调用客户端方法Call client methods from hub
若要从中心接收消息,请使用 HubConnection
的on方法定义方法。
- JavaScript 客户端方法的名称。在下面的示例中,方法名称是
ReceiveMessage
。 - 该中心将传递给方法的参数。在下面的示例中,参数值是
message
。
connection.on("ReceiveMessage", (user, message) => {
const encodedMsg = user + " says " + message;
const li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
当服务器端代码使用SendAsync方法调用时,connection.on
中的前面的代码将运行。
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
SignalR 通过匹配 SendAsync
和 connection.on
中定义的方法名称和参数来确定要调用的客户端方法。
备注
最佳做法是在 on
后对 HubConnection
调用start方法。这样做可确保您的处理程序注册之前接收任何消息。
错误处理和日志记录Error handling and logging
将 catch
方法链接到 start
方法的末尾,以处理客户端错误。使用 console.error
将错误输出到浏览器的控制台。
*/
/* this is here to show another alternative to start, with a catch
通过传递要进行连接时记录的记录器和事件类型的安装程序客户端的日志跟踪。使用指定的日志级别和更高版本记录的消息。可用日志级别为按如下所示:
signalR.LogLevel.Error
– 错误消息。仅记录Error
消息。signalR.LogLevel.Warning
– 有关潜在错误的警告消息。记录Warning
和Error
消息。signalR.LogLevel.Information
– 状态消息而不发生错误。记录Information
、Warning
和Error
消息。signalR.LogLevel.Trace
– 跟踪消息。记录所有内容,包括数据中心和客户端之间传输。
使用HubConnectionBuilder上的configureLogging方法配置日志级别。消息会记录到浏览器控制台。
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.configureLogging(signalR.LogLevel.Information)
.build();
重新连接客户端Reconnect clients
自动重新连接Automatically reconnect
可以将 SignalR 的 JavaScript 客户端配置为使用HubConnectionBuilder上的 withAutomaticReconnect
方法自动重新连接。默认情况下,它不会自动重新连接。
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect()
.build();
如果没有任何参数,withAutomaticReconnect()
会将客户端配置为分别等待0、2、10和30秒,然后再尝试重新连接尝试。
在开始任何重新连接尝试之前,HubConnection
将转换为 HubConnectionState.Reconnecting
状态,并激发其 onreconnecting
回调,而不是转换到 Disconnected
状态并触发其 onclose
回调,如未配置自动重新连接的 HubConnection
。这为用户提供警告连接已丢失并禁用 UI 元素的机会。
connection.onreconnecting((error) => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messagesList").appendChild(li);
});
如果客户端在其前四次尝试内成功重新连接,则 HubConnection
将转换回 Connected
状态,并激发其 onreconnected
回调。这为用户提供了通知用户连接已重新建立的机会。
由于连接完全是服务器的新内容,因此向 onreconnected
回调提供了一个新的 connectionId
。
警告
如果 HubConnection
配置为跳过协商,则不会定义 onreconnected
回调的 connectionId
参数。
connection.onreconnected((connectionId) => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messagesList").appendChild(li);
});
withAutomaticReconnect()
不会将 HubConnection
配置为重试初始启动失败,因此,需要手动处理启动失败:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("connected");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
如果客户端在其前四次尝试中未成功重新连接,则 HubConnection
将转换为 Disconnected
状态,并激发其onclose回调。这为用户提供了通知用户连接永久丢失的机会,并建议刷新页面:
connection.onclose((error) => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messagesList").appendChild(li);
});
为了在断开连接或更改重新连接时间安排之前配置自定义的重新连接尝试次数,withAutomaticReconnect
接受一个数字数组,表示在开始每次重新连接尝试之前等待的延迟(以毫秒为单位)。
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
前面的示例将 HubConnection
配置为在连接丢失后立即开始尝试重新连接。这也适用于默认配置。
如果第一次重新连接尝试失败,则第二次重新连接尝试还会立即启动,而不是等待2秒,就像在默认配置中一样。
如果第二次重新连接尝试失败,则第三次重新连接尝试将在10秒内启动,这与默认配置相同。
然后,在第三次重新连接尝试失败后,自定义行为将再次从默认行为与其分离,而不是在另一个30秒内尝试再次尝试重新连接,就像在默认配置中一样。
如果需要更好地控制计时和自动重新连接尝试的次数,withAutomaticReconnect
接受一个实现 IRetryPolicy
接口的对象,该对象具有名为 nextRetryDelayInMilliseconds
的单个方法。
nextRetryDelayInMilliseconds
采用 RetryContext
类型的单个自变量。RetryContext
具有三个属性: previousRetryCount
、elapsedMilliseconds
和 retryReason
分别为 number
、number
和 Error
。第一次重新连接尝试之前,previousRetryCount
和 elapsedMilliseconds
均为零,retryReason
将是导致连接丢失的错误。每次重试失败后,previousRetryCount
会递增1,elapsedMilliseconds
将更新以反映到目前为止的重新连接时间(以毫秒为单位),retryReason
将是导致上次重新连接尝试失败的错误。
nextRetryDelayInMilliseconds
必须返回一个数字,该数字表示在下一次重新连接尝试之前要等待的毫秒数,或者,如果 HubConnection
应停止重新连接,则为 null
。
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
或者,你可以编写将手动重新连接客户端的代码,如手动重新连接中所示。
手动重新连接Manually reconnect
警告
在3.0 之前,SignalR 的 JavaScript 客户端不会自动重新连接。必须编写代码将手动重新连接你的客户端。
下面的代码演示典型的手动重新连接方法:
- 创建函数(在本例中为
start
函数)以启动连接。 - 在连接的
onclose
事件处理程序中调用start
函数。
async function start() {
try {
await connection.start();
console.log("connected");
} catch (err) {
console.log(err);
setTimeout(() => start(), 5000);
}
};
connection.onclose(async () => {
await start();
});
实际的实现会使用指数退让或重试的次数后放弃了指定的次数。