SignalR收到服务器关闭消息错误,注册监听方法后连接异常如何解决
你遇到的报错核心是SignalR无法序列化Task类型对象,结合你贴的代码,具体有3个必改问题:
- 前端异步逻辑顺序错误:
hubConnection.start()是异步方法,你没有等待连接完成就直接读取connectionId、赋值connection对象,会偶发获取到空值,部分场景下也会导致监听事件注册失效。 - 后端异步方法未等待执行完成就传参:你在
OnConnectedAsync、OnDisconnectedAsync里调用_elasticRepository.GetAllAsync(_indexName)后没有加await,直接把未完成的Task对象传给了SendAsync方法,SignalR尝试序列化Task时就会抛出你截图里的错误。你注释掉UserConnected相关逻辑后不会触发序列化Task的操作,所以程序能正常运行。 - 后端重写的连接生命周期方法未正确await父类实现,且无Cookie分支漏了父类方法调用,会导致连接状态异常,另外你手动new的HttpContextAccessor无法正常拿到当前请求上下文,需要从容器注入。
修改后的Vue代码
// 外层方法需要标注async,比如写在async mounted()或者自定义async方法中 const hubConnection = new signalR.HubConnectionBuilder() .configureLogging(signalR.LogLevel.Debug) .withUrl("http://localhost:7002/ChatHub", { skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets, }) .build(); // 先注册所有监听事件再启动连接,避免连接成功后事件漏发 hubConnection.on("UserConnected",(users) => console.log(users)); hubConnection.on(categoryId, (all) => { all = JSON.parse(all); this.userMessage = { id: all.Id, text: all.Text, userId:all.UserId, userName:all.UserName, categoryName: all.CategoryName, createdOn: all.CreatedOn, }; this.messages.push(this.userMessage); console.log(this.userMessages); }); // 等待连接成功后再读取连接ID await hubConnection.start(); this.connectionId = hubConnection.connectionId; this.connection = hubConnection;
修改后的C# ChatHub代码
首先需要在Program.cs中注册HttpContextAccessor:
builder.Services.AddHttpContextAccessor();
再修改ChatHub实现:
using Microsoft.AspNetCore.SignalR; using System.Text; using Microsoft.AspNetCore.Http; using System.Net.Http.Headers; using System.Linq; using System.IdentityModel.Tokens.Jwt; using System.Collections.Generic; using System.Threading.Tasks; using Message.Dal.Abstract; using Message.Dal.Model; using Microsoft.Extensions.Configuration; namespace Message.Dal.SignalRHub { public class ChatHub : Hub { private readonly IHubContext<ChatHub> _chatHub; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IElasticRepository<OnlineUserModel> _elasticRepository; private readonly string _indexName; public ChatHub(IHubContext<ChatHub> chatHub,IElasticRepository<OnlineUserModel> elasticRepository,IConfiguration configuration, IHttpContextAccessor httpContextAccessor) { _chatHub = chatHub; // 从构造函数注入HttpContextAccessor,不要手动new _httpContextAccessor = httpContextAccessor; _elasticRepository = elasticRepository; _indexName = configuration["elasticsearchserver:User"].ToString(); } public async Task SendMessage(Guid categoryId,string message) { await _chatHub.Clients.All.SendAsync(categoryId.ToString(),message); } public override async Task OnConnectedAsync() { var token = string.Empty; var httpContext = _httpContextAccessor.HttpContext.Request.Cookies; var onlineUserModel = new OnlineUserModel(); onlineUserModel.Id = Context.ConnectionId; if(httpContext.Any()) { token = httpContext.FirstOrDefault(x=> x.Key == "CodeChatBackend").Value; if(AuthenticationHeaderValue.TryParse(token,out var headerVal)) { var handler = new JwtSecurityTokenHandler(); var val = handler.ReadJwtToken(headerVal.ToString()); onlineUserModel.UserName = val.Claims.FirstOrDefault(x => x.Type == "Name").Value; } var checkOnlineUser = await _elasticRepository.GetUserAsync(onlineUserModel.UserName,_indexName); if(checkOnlineUser == null) { await _elasticRepository.CreateUserAsync(onlineUserModel.Id,onlineUserModel,_indexName); } var getOnlineUser = await _elasticRepository.GetAllAsync(_indexName); await _chatHub.Clients.All.SendAsync("UserConnected",getOnlineUser); } // 无论是否有Cookie都要调用父类连接逻辑 await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception exception) { await _elasticRepository.DeleteUserAsync(Context.ConnectionId,_indexName); var getOnlineUser = await _elasticRepository.GetAllAsync(_indexName); await _chatHub.Clients.All.SendAsync("UserConnected",getOnlineUser); await base.OnDisconnectedAsync(exception); } } }
内容的提问来源于stack exchange,提问作者OrhanVeliGOGEBAKAN
相关产品推荐
相关产品推荐

