In this post, let's have a look at how to send user Notifications using Azure Functions SignalR Service output binding.
I already have an Azure SignalR Service and Azure Function setup. My client app is an Angular application.
In my Azure Function, I have the Negotiate function as follows.
public static class NegotiateFunction { [FunctionName(FunctionNames.Negotiate)] public static SignalRConnectionInfo Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest request, [SignalRConnectionInfo( HubName = "%SignalR_HubName%", UserId = "{headers.x-application-user-id}", ConnectionStringSetting = "SignalR_ConnectionString")] SignalRConnectionInfo connectionInfo) { return connectionInfo; } }
Note I am setting the UserId to a custom header (x-application-user-id) I am expecting the client app to send.
Alternatively, you can use imperative binding instead of declarative binding (which is done above). This is helpful when you have clients who aren't sending the header you are looking for UserId. With declarative binding, if the header x-application-user-id isn't set, Negotiate function will throw an error.
Imperative binding can be done as follows.
public class NegotiateFunction { private readonly IConfiguration _configuration; public NegotiateFunction(IConfiguration configuration) { _configuration = configuration; } [FunctionName(FunctionNames.Negotiate)] public async Task<SignalRConnectionInfo> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest request, IBinder binder) { string userId = request.Headers["x-application-user-id"]; SignalRConnectionInfoAttribute attribute = new SignalRConnectionInfoAttribute { HubName = _configuration.GetValue<string>("SignalR_HubName"), UserId = userId, ConnectionStringSetting = "SignalR_ConnectionString" }; SignalRConnectionInfo connectionInfo = await binder.BindAsync<SignalRConnectionInfo>(attribute); return connectionInfo; } }
And on my client app, I am building the hubConnection as follows.
this.hubConnection = new signalR.HubConnectionBuilder() .withUrl("<azure-functions-base-url>", { headers: { "x-functions-key": "<function key>", "x-application-user-id": "<user-id>" } }) .withAutomaticReconnect() .configureLogging(signalR.LogLevel.Information) .build();
Once that is done and the connection is started, we should be seeing the UserId in the access token when the client negotiates with Azure SignalR Service (after negotiating with Function App and receiving the connection info).
Access Token |
Now we just need to set the UserId when adding a SignalRMessage via the output binding in our Function App, something like this.
public static class SendApplicationUserNotificationFunction { [FunctionName(FunctionNames.SendApplicationUserNotification)] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest request, [SignalR( HubName = "%SignalR_HubName%", ConnectionStringSetting = "SignalR_ConnectionString")]IAsyncCollector<SignalRMessage> signalRMessageCollector) { var requestBody = await new StreamReader(request.Body).ReadToEndAsync(); ApplicationUserNotification applicationUserNotification = JsonConvert.DeserializeObject<ApplicationUserNotification>(requestBody); await signalRMessageCollector.AddAsync(new SignalRMessage { Target = SignalRTargets.AddApplicationUserNotification, Arguments = new[] { applicationUserNotification }, UserId = applicationUserNotification.ApplicationUserId, }); return new OkResult(); } }
So I have two users logged in to my client application, UserId: 1 and 110. And I am sending a message to UserId: 1.
I can see UserId: 1 received the message.
UserId: 1 received the message |
UserId: 110 didn't receive the message |
Happy Coding.
Regards,
Jaliya