ASP.NET Core 8中SignalR的UnsubscribeFromWatchlist方法未触发问题
在ASP.NET Core 8环境下的SignalR服务端定义了两个逻辑几乎一致的待调用方法,仅SubscribeToWatchlist方法在客户端调用时能触发服务端执行,UnsubscribeFromWatchlist方法却无法触发。
触发Unsubscribe方法时,Chrome开发者工具显示请求内容为{"arguments":[1],"invocationId":"7","target":"UnsubscribeFromWatchlist","type":1}{"type":7},说明客户端连接及请求均正常,但服务端的VS断点和Debug.WriteLine均无响应。
服务端代码
public async Task SubscribeToWatchlist(int watchlistType) { int userId = int.Parse(Context.User.Identity.Name); string connectionId = Context.ConnectionId; if (_watchlistSubscriptions.TryAdd(Context.ConnectionId + watchlistType, new CancellationTokenSource())) { await _realtimeService.MonitorWatchlist(watchlistType, userId, connectionId, _watchlistSubscriptions[Context.ConnectionId + watchlistType].Token); } } public void UnsubscribeFromWatchlist(int watchlistType) { int userId = int.Parse(Context.User.Identity.Name); string connectionId = Context.ConnectionId; //await Groups.RemoveFromGroupAsync(Context.ConnectionId, "watchlist-" + watchlistType); if (_watchlistSubscriptions.TryRemove(Context.ConnectionId + watchlistType, out var cts)) { _realtimeService.StopMonitoringWatchlist(watchlistType, userId, connectionId, cts.Token); cts.Cancel(); cts.Dispose(); } }
客户端JavaScript代码
async subscribeToWatchlist() { await this.connection.invoke("SubscribeToWatchlist", 1); }, async unsubscribeFromWatchlist() { await this.connection.invoke("UnsubscribeFromWatchlist", 1); },
可能的原因及解决方案
方法返回值不符合SignalR要求:SignalR规定,客户端通过
invoke调用的服务端方法必须返回Task或Task<T>类型。UnsubscribeFromWatchlist当前返回void,会导致SignalR无法正确处理异步调用的完成信号,进而出现服务端方法不触发的情况。修改方法返回类型为Task,即使没有异步操作,也返回Task.CompletedTask:public Task UnsubscribeFromWatchlist(int watchlistType) { int userId = int.Parse(Context.User.Identity.Name); string connectionId = Context.ConnectionId; if (_watchlistSubscriptions.TryRemove(Context.ConnectionId + watchlistType, out var cts)) { _realtimeService.StopMonitoringWatchlist(watchlistType, userId, connectionId, cts.Token); cts.Cancel(); cts.Dispose(); } return Task.CompletedTask; }潜在异常未被捕获:如果
Context.User.Identity.Name解析失败(比如用户身份信息丢失),void方法抛出的异常无法被SignalR捕获并反馈给客户端,会表现为服务端方法看似未触发。改为Task返回后,异常会被SignalR传递到客户端,可通过客户端的catch块排查问题:async unsubscribeFromWatchlist() { try { await this.connection.invoke("UnsubscribeFromWatchlist", 1); } catch (err) { console.error(err); } },同步操作阻塞线程:若
_realtimeService.StopMonitoringWatchlist是耗时的同步阻塞方法,可能占用SignalR的处理线程,导致方法无法正常执行。可将该方法改为异步,或在异步线程中执行同步操作避免阻塞。
内容的提问来源于stack exchange,提问作者Maya

