本文介绍:

Azure Functions 和SignalR service 实现广播/组播/单独发送给指定用户。

 

视频讲解:

 

图文步骤:

创建SignalR Service:

 

填写信息,完成创建:

 

找到SignalR 连接字符串:

 

 

Functions完整代码:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

using Microsoft.Azure.WebJobs.Extensions.SignalRService;

namespace Company.Function
{
    public static class HttpTriggerCSharp1
    {
        


       

        /// <summary>
        /// 协商函数,已经完成身份认证的客户端,携带header[x-ms-client-principal-id]登录signalr 服务
        /// </summary>
        /// <param name="req"></param>
        /// <param name="connectionInfo"></param>
        /// <returns></returns>
        [FunctionName("negotiate")]
        public static SignalRConnectionInfo GetSignalRInfo(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
            [SignalRConnectionInfo(HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")] SignalRConnectionInfo connectionInfo)
        {
            
            return connectionInfo;
        }


        //广播消息
        [FunctionName("messages")]
        public static Task SendMessage(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                  
                    Target = "newMessage",
                    Arguments = new[] { message }
                }); ;
        }



        //给指定的用户“user1” 发送消息
        [FunctionName("messagestouser")]
        public static Task SendMessageToUser(
          [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
          [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    UserId = "user1",
                    Target = "newMessage",
                    Arguments = new[] { message }
                }); ;
        }


        //给指定的组 发送消息
        [FunctionName("messagestogroup")]
        public static Task SendMessageToGroup(
          [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
          [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    GroupName = "group1",
                    Target = "newMessage",
                    Arguments = new[] { message }
                }); ;
        }


        //将用户添加到组
        [FunctionName("AddToGroup")]
        public static Task AddToGroup(
           [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req,
           ILogger log,
           [SignalR(HubName = "chat")]IAsyncCollector<SignalRGroupAction> signalRGroupActions)
        {
            string userId = req.Query["UserId"];
            string groupId = req.Query["GroupId"];
            return signalRGroupActions.AddAsync(
                 new SignalRGroupAction
                 {
                     UserId = userId,
                     GroupName = groupId,
                     Action = GroupAction.Add
                 });
        }


        //将用户从组中删除
        [FunctionName("RemoveFromGroup")]
        public static Task removeFromGroup(
           [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req,
           ILogger log,
           [SignalR(HubName = "chat")]IAsyncCollector<SignalRGroupAction> signalRGroupActions)
        {
            string userId = req.Query["UserId"];
            string groupId = req.Query["GroupId"];
            return signalRGroupActions.AddAsync(
                 new SignalRGroupAction
                 {
                     UserId = userId,
                     GroupName = groupId,
                     Action = GroupAction.Remove
                 });
        }
      
    }
}

 

Function中关于SignalR的配置:

 

客户端完整代码:

客户端代码,本例中使用了localhost的调试function,SDK会自动寻找“negotiate”函数并向negotiate发起请求:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
//using Microsoft.Azure.WebJobs.Extensions.SignalRService;

using System.Collections.Generic;
using Newtonsoft.Json;

namespace ConsoleApp1
{
    class Program
    {  



        static async Task Main(string[] args)
        {
            Console.WriteLine("Hello world!");

            var UserID = Console.ReadLine();

            var connection = new HubConnectionBuilder().WithUrl("http://localhost:7071/api", 
                options => options.Headers = new Dictionary<string, string>
                    {
                        {
                            "x-ms-client-principal-id", UserID
                        }
                    }).Build();

            connection.On<Object>("newMessage", ( Object message) =>
            {
                Console.WriteLine($"Message from server : {message}");
            }
            );
            await connection.StartAsync();
            Console.ReadKey();
        }

    }


}

客户端运行时,需要输入一个名称,如下图所示:

本例中依次登录user1,user2,user3:

 

协商函数:

协商函数是使用Functions和SignalR service的关键,客户端通过协商函数获取到SignalR Service 的连接信息,

其中的UserId = "{headers.x-ms-client-principal-id},是由客户端调用SDK时加入的字段,注意,此字段并非一定要命名为“x-ms-client-principal-id”,但要求上下文一致,即Functions和客户端使用同样的字段。

  /// <summary>
        /// 协商函数,已经完成身份认证的客户端,携带header[x-ms-client-principal-id]登录signalr 服务
        /// </summary>
        /// <param name="req"></param>
        /// <param name="connectionInfo"></param>
        /// <returns></returns>
        [FunctionName("negotiate")]
        public static SignalRConnectionInfo GetSignalRInfo(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
            [SignalRConnectionInfo(HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")] SignalRConnectionInfo connectionInfo)
        {
            
            return connectionInfo;
        }

 

广播给所有用户:

注意,广播的SignalRMessage里不指定任何UserID或GroupID即可实现向所有在线客户端发送广播

 //广播消息
        [FunctionName("messages")]
        public static Task SendMessage(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                  
                    Target = "newMessage",
                    Arguments = new[] { message }
                }); ;
        }

 

使用Postman发送广播请求:

注意,请求的消息体Body部分可以随意填写,本例中的格式不是必须的。

 

可以观察到广播消息如下:

 

单独发给某个用户:

注意,本例代码中是hardcode写死指定发给user1,实际上可以在http请求中传递要发给的用户ID:

所谓的单独发送给某个用户,只要在SignalRMessage中增加UserID属性即可。

//给指定的用户“user1” 发送消息
        [FunctionName("messagestouser")]
        public static Task SendMessageToUser(
          [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
          [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    UserId = "user1",
                    Target = "newMessage",
                    Arguments = new[] { message }
                }); ;
        }

 

使用PostMan发送请求:

观察结果,只有user1收到单独发送的消息:

 

群发给指定的组:

在执行群发给指定的组之前,需要先将用户添加到组,否则,组里没有用户的话,是看不出发送效果的。

 

将用户添加到组:

//将用户添加到组
        [FunctionName("AddToGroup")]
        public static Task AddToGroup(
           [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req,
           ILogger log,
           [SignalR(HubName = "chat")]IAsyncCollector<SignalRGroupAction> signalRGroupActions)
        {
            string userId = req.Query["UserId"];
            string groupId = req.Query["GroupId"];
            return signalRGroupActions.AddAsync(
                 new SignalRGroupAction
                 {
                     UserId = userId,
                     GroupName = groupId,
                     Action = GroupAction.Add
                 });
        }

将user1添加到group1:

 

将user2添加到group1:

 

向指定的组发送消息:

注意,本例中hardcode 规定给组”group1“发送,只要在SignalRMessage中增加GroupName属性即可。

 //给指定的组 发送消息
        [FunctionName("messagestogroup")]
        public static Task SendMessageToGroup(
          [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
          [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    GroupName = "group1",
                    Target = "newMessage",
                    Arguments = new[] { message }
                }); ;
        }

 

PostMan发送给组:

结果如下:

group1中包含user1和user2都收到了消息,user3不在group1中,故而没收到消息。