作用
使用Signalr可实现前后端互相主动给对方推送消息,不再是前端请求后端响应的单向模式。
Signalr会根据实际情况自动选择以下方式实现:WebSockets、Server-Sent Events、Long Polling。
代码GitHub地址:SomeExperiments
后端实现
(注:这用到了我自己写的一个nuget包【tdb.framework.webapi Version=“6.0.1.1” 】,但在这个版本还没把SignalR封装进去)
- 修改Startup.cs
以下代码主要关注注释中带有SignalR的代码
public void ConfigureServices(IServiceCollection services)
{
//日志
services.AddTdbNLogger();
//设置允许所有来源跨域
services.AddTdbAllAllowCors();
//添加身份认证与验权服务
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = tdb.framework.webapi.standard.Auth.TdbClaimTypes.Name,
RoleClaimType = tdb.framework.webapi.standard.Auth.TdbClaimTypes.Role,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("tangdabin20220108")),
//不验Audience
ValidateAudience = false,
//不验Issuer
ValidateIssuer = false,
//允许的服务器时间偏移量
ClockSkew = TimeSpan.FromSeconds(10),
};
o.Events = new JwtBearerEvents()
{
OnMessageReceived = (context) =>
{
if (!context.HttpContext.Request.Path.HasValue)
{
return Task.CompletedTask;
}
//这里是用来给Signalr验证身份信息用的
var accessToken = context.HttpContext.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!(string.IsNullOrWhiteSpace(accessToken)) && path.StartsWithSegments("/AuthHub"))
{
context.Token = accessToken;
return Task.CompletedTask;
}
return Task.CompletedTask;
}
};
});
//授权
services.AddAuthorization();
//添加api版本控制及浏览服务
services.AddTdbApiVersionExplorer();
//swagger
services.AddTdbSwaggerGenApiVer(o =>
{
o.ServiceCode = "tdb.signalR.demo";
o.ServiceName = "SignalR Demo";
o.LstXmlCommentsFileName.Add("SignalRService.xml");
});
//SignalR
services.AddSignalR();
//services.AddControllers();
services.AddControllers(option =>
{
//异常处理
option.AddTdbGlobalException();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
//设置允许所有来源跨域
app.UseTdbAllAllowCors();
//认证
app.UseAuthentication();
//授权
app.UseAuthorization();
//swagger
app.UseTdbSwaggerAndUIApiVer(provider);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
//前端连接Signalr的路由地址
endpoints.MapHub<ServerTimeHub>("/ServerTimeHub");
endpoints.MapHub<SendMsgHub>("/SendMsgHub");
endpoints.MapHub<AuthHub>("/AuthHub");
});
//SignalR广播服务器时间
BroadcastTime(app);
}
//SignalR广播服务器时间
private void BroadcastTime(IApplicationBuilder app)
{
Task.Factory.StartNew(async () =>
{
while (true)
{
var hubContext = app.ApplicationServices.GetService<IHubContext<ServerTimeHub, IServerTimeHub>>();
await hubContext.Clients.All.BroadcastTime(DateTime.Now);
await Task.Delay(1000);
}
});
}
- 继承Hub
继承Hub写自己的业务处理(一个Hub一个链接)
具体看已上传到github上的代码。
前端实现(VUE)
- 安装依赖
npm install @microsoft/signalr
- 引用对象
const signalR = require("@microsoft/signalr");
- 链接
不带token
this.connMsg = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:36921/SendMsgHub", { })
.configureLogging(signalR.LogLevel.Information)
.build();
带token
this.connAuth = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:36921/AuthHub", { accessTokenFactory: () => this.authToken })
.configureLogging(signalR.LogLevel.Trace)
.build();
- 监听后端事件
this.connAuth.on("ReceiveMessage", data => {
console.log("ReceiveMessage"+data);
});
- 向后端发送消息
this.connMsg.invoke("SendToAll", this.text);
- 生命周期
this.connection.onreconnecting(error => {
console.log("onreconnecting:"+error);
});
this.connection.onreconnected(connectionId => {
console.log("onreconnected:"+connectionId);
});
this.connection.onclose(error => {
console.log("onclose:"+error);
});
具体看已上传到github上的代码。