I beleive I have some mis-conceptions here in how Azure SignalR is meant to work when deploying an Azure Function that uses a SignalR Output Binding.
My environment:
1 - Azure Function which does some work and then sends a message to the SignalR service.
2 - A NET Core Web App is connected to Azure SignalR service and should receive these notifications.
The MS examples seem to suggest that the Net Core Webb App has to negotiate for a token and connect directly with the Azure Function in order to receive those messages. But this is now what I'm trying to achieve.
Given both applications connect with Azure SignalR service using the secure key/connection string, I would have expected Azure to act as the middle man and pass messages between one end to the other. Is this correct or have I got this competely wrong?
As a basic exmaple: Azure Function is configured similar to below where I receive a message from a service bus queue, on receipt of this message I then send the same message to Azure SignalR Service:
[FunctionName("My-Function-Name")]
public async Task RunAsync([ServiceBusTrigger(
"sampleQueue", Connection = "AzureServiceBusConnetion")]
string message,
[SignalR(HubName = "macroHub")] IAsyncCollector<SignalRMessage> signalRMessage,
ILogger logger)
{
logger.LogInformation($"C# ServiceBus queue trigger function processed message: {message}");
await signalRMessage.AddAsync(
new SignalRMessage
{
Target = "newMessage",
Arguments = new[] { message }
});
}
So from the above, no method required for negotiate or supplying any connection to an external client with a token i.e. I dont expect to have to connect my NET Core Web App directly to this Azure function. The message is simply sent to the Azure Signal service and Azure grabs this message.
Next, the NET Core Web App is connected to Azure SignalR using the Nuget package and a local Hub class is configured. On one of the pages of my NET Core Web App, I connect to the local Hub via JavaScript and should receive the message routed through Azure after it was sent out from the Azure Function.
NET Core Web App:
public class MacroHub : Hub
{
public async Task ReceiveMessage(string message)
{
await Clients.All.SendAsync("newMessage", message);
}
}
Sample JS in the Net Core Web App:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/macroHub")
.withAutomaticReconnect()
.build();
connection.on("newMessage", (message) => {
alert(message)
});
This is not really a question about the code as such, I've had SignalR working fine before when using it locally in just one NET Core Web App, but I'm trying to utilise the Azure SignalR service in order to connect messages sent from an Azure Function through the Cloud and then receive those messages in the Net Core Web App.
Everything I've tried so far doesnt show any activity, neither the Web App of the Azure portal shows any logs or events for messages received.
Issues Fixed! so my assumptions was in fact correct but I had some issues in the code of my test scenario, so below I've shown a working example. I've adapted one of the MS Examples so the code exmaple is different from what was shown in my original question, but at least the below works so can move forward from here...
IMPORTANT NOTE: After scratching head and having no *** hair left! in order for the example below to work, I had to change the mode of the SignalR Service in Azure from serverless to default:
Azure Function App Code
Function.cs File:
The function below is running on a trigger timer, so every 5 seconds I generate a simple test message "This is a test message!" and we send this to the Azure SignalR Service.
namespace CSharp
{
public static class Function
{
[FunctionName("broadcast")]
public static async Task Broadcast([TimerTrigger("*/5 * * * * *")] TimerInfo myTimer,
[SignalR(HubName = "serverless")] IAsyncCollector<SignalRMessage> signalRMessages)
{
await signalRMessages.AddAsync(
new SignalRMessage
{
Target = "newMessage",
Arguments = new[] { "This is a test message!" }
});
}
}
}
Azure Function local.settings.json file:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"AzureSignalRConnectionString": "Endpoint=https://myDomain-signalr.service.signalr.net;AccessKey=youarenothavingmysecretkey=;Version=1.0;"
},
"ConnectionStrings": {}
}
.NET 6 Web App Code
Firstly make sure the SignalR Client Library is installed from Nuget:
Then install the SignalR Client Side Library or reference a CDN
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
Or see link: https://learn.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-6.0&tabs=visual-studio
Create the connection string, I use appsettings.json for test/development
"Azure": {
"SignalR": {
"ConnectionString": "Endpoint=https://myDomain-signalr.service.signalr.net;AccessKey=youarenothavingmysecretkey=;Version=1.0;"
}
}
Then create the SignalR Hub Class:
public class Serverless : Hub
{
public async Task NewMessage(string message)
{
await Clients.All.SendAsync("newMessage", message);
}
}
Program.cs File (I'm using .NET 6 minimal startup pattern i.e. no Startup.cs File)
// Add the Azure SignalR Service.
builder.Services.AddSignalR().AddAzureSignalR(builder.Configuration["Azure:SignalR:ConnectionString"]);
var app = builder.Build();
app.MapHub<Serverless>("/serverless");
Sample Web page in the .NET 6 Web App (JavaScript code section)
const connection = new signalR.HubConnectionBuilder()
.withUrl("/serverless")
.withAutomaticReconnect()
.build();
connection.on("newMessage", (message) => {
alert(message)
});
// We need an async function in order to use await, but we want this code to run immediately,
// so we use an "immediately-executed async function"
(async () => {
try {
await connection.start();
}
catch (e) {
console.error(e.toString());
}
})();
Testing the example:
I first fire up my .NET Core Web App and load the sample webpage that holds the above javaScript code, the JS code connects with the local SignalR Hub which in turn then connects with the Azure SignalR Service, so in essence i have my connection to Azure:
The browser debug shows the connection established:
I then fire up the Azure Function App (locally, not deployed to Azure yet)
And finally, my Web App shows the test alert message popup each time the Azure Function sends out the test message to the Azure SignalR Service:
Boom! :)