Asp.Net Core 外部服务向SignalR的Hub发送消

发布于:2025-06-11 ⋅ 阅读:(23) ⋅ 点赞:(0)


前言

在 ASP.NET Core 中,若需要从 外部服务(如 Web API、后台任务或其他微服务)向 SignalR 的 Hub 发送消息,可以通过以下方式实现:利用 IHubContext 服务直接调用 Hub 方法,无需客户端连接上下文。

一、使用步骤

1.在服务类中使用

1)注入 IHubContext

在需要发送消息的类(如控制器、服务或后台任务)中,通过依赖注入获取 IHubContext 对象。IHubContext 允许从服务器端主动向客户端发送消息,无需通过 Hub 类本身。

  1. 示例:

    using Microsoft.AspNetCore.SignalR;
    	
    public class NotificationService
    {
        private readonly IHubContext<MyHubService> _hubContext; // MyHubService是你的 SignalR Hub 类
    
        public NotificationService(IHubContext<MyHubService> hubContext)
        {
            _hubContext = hubContext;
        }
    }
    

2)向所有连接的客户端发送消息

  1. 示例:

    using Microsoft.AspNetCore.SignalR;
    
    public class NotificationService
    {
        private readonly IHubContext<MyHubService> _hubContext; // MyHubService是你的 SignalR Hub 类
    
        public NotificationService(IHubContext<MyHubService> hubContext)
        {
            _hubContext = hubContext;
        }
    
        public async Task SendMessageAsync(string message)
        {
            // 向所有连接的客户端发送消息
            await _hubContext.Clients.All.SendAsync("ReceiveMsg", message);
        }
    }
    

3)向特定客户端发送消息

通过 IHubContextClients 属性,结合连接 ID用户 ID组名,向部分客户端发送消息。

根据连接ID发送消息:
  1. 示例:
    using Microsoft.AspNetCore.SignalR;
    
    public class NotificationService
    {
        private readonly IHubContext<MyHubService> _hubContext; // MyHubService是你的 SignalR Hub 类
    
        public NotificationService(IHubContext<MyHubService> hubContext)
        {
            _hubContext = hubContext;
        }
    
        public async Task SendPrivateMessageAsync(string connectionId,string message)
        {
            // 根据连接Id发送消息
            await _hubContext.Clients.Client(connectionId).SendAsync("ReceivePrivateMsg", message);
        }
    }
    
按用户 ID 发送(需身份验证)

若客户端已通过身份验证并关联了用户 ID,可通过 UserIdentifier 发送消息(需在 Hub 中配置用户映射):

  1. 示例:
    using Microsoft.AspNetCore.SignalR;
    
    public class NotificationService
    {
        private readonly IHubContext<MyHubService> _hubContext; // MyHubService是你的 SignalR Hub 类
    
        public NotificationService(IHubContext<MyHubService> hubContext)
        {
            _hubContext = hubContext;
        }
    
        public async Task SendUserMessageAsync(string userId,string message)
        {
            // 向所有属于该用户的连接发送消息(适用于多设备登录)
            await _hubContext.Clients.User(userId).SendAsync("ReceiveUserMsg", message);
        }
    }
    
    注意:使用 Clients.User(userId) 时,需确保客户端连接已关联用户 ID(通过认证声明,如 ClaimTypes.NameIdentifier)。
按组发送消息
  1. 示例:
    using Microsoft.AspNetCore.SignalR;
    
    public class NotificationService
    {
        private readonly IHubContext<MyHubService> _hubContext; // MyHubService是你的 SignalR Hub 类
    
        public NotificationService(IHubContext<MyHubService> hubContext)
        {
            _hubContext = hubContext;
        }
    
        public async Task SendGroupMessageAsync(string groupName,string message)
        {
            // 向所有属于该组的用户发送消息
            await _hubContext.Clients.Group(groupName).SendAsync("ReceiveGroupMsg", message);
        }
    }
    

2.在 Controller 中使用 IHubContext

Web API 控制器中注入 IHubContext,实现通过 HTTP 请求触发 SignalR 消息发送。

API 控制器发送消息

  1. 示例:

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.SignalR;
    using SignalRDemo.Entity;
    using SignalRDemo.HubService;
    
    namespace SignalRDemo.Controllers
    {
        [Route("api/[controller]/[action]")]
        [ApiController]
        public class TestController : ControllerBase
        {
            private readonly IHubContext<MyHubService> _hubContext;
            private readonly UserManager<User> _userManager;
            private readonly RoleManager<Role> _roleManager;
    
            public TestController(IHubContext<MyHubService> hubContext, UserManager<User> userManager, RoleManager<Role> roleManager, IWebHostEnvironment webHostEnvironment)
            {
                _hubContext = hubContext;
                _userManager = userManager;
                _roleManager = roleManager;
            }
            [HttpPost]
            public async Task<IActionResult> AddNewUser(LoginModel newuser)
            {
                User user = new User { UserName= newuser.Username, CreateTime = DateTime.Now.ToString() };
                var res = await _userManager.CreateAsync(user, newuser.Password) ;
                if (!res.Succeeded)
                {                
                    return BadRequest("CreateUserAsync Failed");
                }
                await _hubContext.Clients.All.SendAsync("ReceiveMsg", $"从Controller发送消:欢迎{user.UserName}加入。");
                return Ok(new { Status = "Message sent", Message = $"从Controller发送消:欢迎{user.UserName}加入。"});
            }
       }
       public class LoginModel
    	{
    	    public string Username { get; set; } = string.Empty;
    	    public string Password { get; set; } = string.Empty;
    	}
    }
    

3.在后台任务中使用 IHubContext

在定时任务或后台服务中(如 BackgroundService),注入 IHubContext 发送异步通知。

后台托管服务发送定时消息

  1. 示例:

    public class NotificationBackgroundService : BackgroundService
    {
        private readonly IHubContext<MyHub> _hubContext;
        private readonly ILogger<NotificationBackgroundService> _logger;
    
        public NotificationBackgroundService(
            IHubContext<MyHubService> hubContext,
            ILogger<NotificationBackgroundService> logger)
        {
            _hubContext = hubContext;
            _logger = logger;
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                var message = $"定时通知:{DateTime.Now}";
                await _hubContext.Clients.All.SendAsync("ReceiveScheduledNotification", message);
                _logger.LogInformation("定时通知已发送: {Message}", message);
                await Task.Delay(5000, stoppingToken); // 每 5 秒发送一次
            }
        }
    }
    
  2. 注册后台托管服务:

    builder.Services.AddHostedService<NotificationBackgroundService>();
    

4.客户端接收消息

在 SignalR 客户端(如浏览器、桌面应用)中,监听对应的方法名:

  1. 示例:

    // JavaScript 客户端示例
    // 创建新连接
    state.connection = new signalR.HubConnectionBuilder()
         .withUrl(state.serverUrl, {
             accessTokenFactory: () => token,
             skipNegotiation: true,
             transport: signalR.HttpTransportType.WebSockets
         })
         .withAutomaticReconnect()
         .configureLogging(signalR.LogLevel.Information)
         .build();
     
     // 注册消息处理程序
     state.connection.on("ReceiveMsg", (user, message) => {
         state.messages.push({
             type: 'broadcast',
             sender: `${user}(广播消息)`,
             content: message,
             timestamp: new Date()
         });
     });
     
     state.connection.on("ReceivePrivateMsg", (sender, message) => {              
       if (!sender || !message) return;
         state.messages.push({
             type: 'private',
             sender: `${sender} (私信)`,
             content: message,
             timestamp: new Date()
         });
     });
     
     state.connection.on("ReceiveGroupMsg", (sender, group, message) => {
         state.messages.push({
             type: 'group',
             sender: `${sender} (${group})`,
             content: message,
             group: group,
             timestamp: new Date()
         });
     });
     //启动连接
     connection.start().catch(err => console.error("连接失败:", err));
    
    

三、关键注意事项

  • 依赖注入范围:
    • IHubContext<THub> 是 scopeless(介于 TransientSingleton 之间),可在瞬态、作用域或单例服务中安全注入。
  • 方法名匹配:
    • 服务端调用的方法名(如 SendAsync(“ReceiveNotification”, message))必须与客户端通过 connection.on() 监听的方法名完全一致。
  • 参数传递:
    • 支持传递任意可序列化的参数(如对象、数组),客户端需按相同顺序接收参数

总结

通过以上方法,可轻松实现从外部服务向 SignalR Hub 发送消息,满足实时通知、状态更新等业务需求。