signalr hub类必须继承hub且仅通过构造函数注入依赖,实例由运行时按需创建、生命周期极短;服务端推送须用ihubcontext,group管理需手动清理断连用户,连接失败优先排查negotiate握手与cors配置。

SignalR Hub类必须继承Hub,且不能有构造函数注入以外的依赖初始化
Hub 实例由 SignalR 运行时按需创建,生命周期极短,每次调用都可能是新实例。直接在字段里 new 对象、或在构造函数里做耗时操作(比如连数据库、读文件),会导致连接失败或响应延迟。
- 依赖只能通过构造函数注入,且必须注册为
Scoped或Transient(Singleton要格外小心线程安全) - 不要在
OnConnectedAsync里 await 长时间任务;真要初始化,改用客户端主动发个Init方法再处理 - Hub 类本身不能被
new Hub()手动实例化——运行时会报InvalidOperationException: Hub has not been initialized
IHubContext<myhub></myhub> 是服务端主动推送的唯一可靠入口
控制器、后台服务、定时任务想推消息,必须通过 IHubContext<myhub></myhub>,而不是自己保存 Hub 引用或试图调用 Clients.All.SendAsync 静态方法(那根本不存在)。
- 在
Program.cs注册时用services.AddSignalR(),然后注入IHubContext<myhub></myhub>即可 - 推送范围别硬编码字符串:用
Clients.All、Clients.User("uid")、Clients.Group("admin"),避免拼错 group name 导致静默失败 - 如果用
Clients.Client(connectionId),务必确认 connectionId 仍有效——断连后 ID 失效,不会报错但消息丢弃
客户端 JS 连接不上?先检查 negotiate 请求返回的 url 和 accessToken
浏览器控制台看到 Failed to start the connection: Error: Failed to complete negotiation with the server,大概率是握手环节出问题,不是 Hub 代码写错了。
- 确保服务器启用了 CORS,且允许
https://your-client-domain(不能只写*如果带凭证) - 如果用了 JWT 认证,
negotiate响应里必须有accessToken字段,客户端要用它初始化连接:new HubConnectionBuilder().withUrl("/hub", { accessTokenFactory: () => token }) - 检查
MapHub<myhub>("/hub")</myhub>是否放在UseRouting()之后、UseEndpoints()之内——顺序错会导致 404
Group 管理容易漏掉 OnDisconnectedAsync 的清理逻辑
用户加入 Group 通常在 OnConnectedAsync 里调 Groups.AddToGroupAsync,但断连时 SignalR 不自动移出,得手动处理,否则群组状态会持续膨胀、推送发给已下线的人。
- 必须重写
OnDisconnectedAsync(Exception? exception),在里面调Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName) - 不要依赖
Context.UserIdentifier做 group 名——它可能为空;建议登录后显式用await Clients.Caller.SendAsync("SetUserId", userId),再让客户端发个JoinGroup请求 - Group 名建议用业务 ID(如
"order-123"),别用随机 GUID,否则没法精准推送和调试










