本文介绍:
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中,故而没收到消息。