1

So I am rather stuck on this. We have been using "Default" SignalR with our Web apps for ages and all is fine.

We now want to add an Azure Function which requires SignalR to be Serverless. I can easily get SignalR and the Azure function to play ball.

However, I can not get the Web App to talk to SignalR as it says

"Critical: Service returned handshake error: Azure SignalR Service is in serverless mode, server connection is not allowed.. Id: fe6a4f57-6293-4b6f-bdec-9040ff60aef5"

Ok so looking at other tutorials like :

https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-dotnet-core https://softchris.github.io/pages/dotnet-signalr.html#configure-signalr

You can do it in Javascript

const connection = new signalR.HubConnectionBuilder()
                .withUrl('https://*****-sb-staging-signalr.service.signalr.net/statushub')
                .build();
            bindConnectionMessage(connection);

However this gives me CORS issues, even though SignalR accepts all and I have in mty Startup.cs

services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder =>
                {
                    builder.WithOrigins("https://******-sb-staging-admin.azurewebsites.net/")
                        .AllowCredentials();
                });
            });

and

services.AddSignalR(srConfig => srConfig.EnableDetailedErrors = true).AddAzureSignalR();
services.AddCors();

How on earth do I get a Web app to talk to SignalR Serverless

Burf2000
  • 5,001
  • 14
  • 58
  • 117

2 Answers2

1

What's the role of your Azure Function? Do you still have Hub logics defined in your Web Apps?

When in Default mode, your Web App handles the lifetime of the clients, the SignalR service acts like a proxy that it routes all the client traffics through server connections to the Web App, while in Serverless mode, the SignalR service handles the lifetime of the clients. This article describes the differences in detail.

If you still have your Web App handle the clients in the Hub class, and Azure Function is simply used to send messages to these clients, please use Default mode.

vicancy
  • 323
  • 3
  • 11
0

The Azure Function side of SignalR Serverless is well covered in the Microsoft documentation.

Getting the Web App wired up is unfortunately not as well documented. Here are some snippets that show what to do to connect a Blazor Server Web App with Azure Signalr Service in Serverless mode.

First, install this package Microsoft.AspNetCore.SignalR.Client into the Web App.

The web app to call the Azure Function's negotiate function to return the url and access token for the SignalR Service. Then you use the url and access token to connect to the serverless hub.

//Call the Azure Function Negotiate endpoint to retrieve the url and access token
var httpClient = _clientFactory.CreateClient();

string url = $"https://your-azure-function-bound-to-signalr.net/negotiate?userid=you@contoso.com";

var response = await httpClient.PostAsync(url, null);

var body = await response.Content.ReadAsStringAsync();

var conn = JsonSerializer.Deserialize<SignalRConnectionInfo>(body);

//Use the url and access token from the Negotiate endpoint to start the SignalR Hub connection
hubConnection = new HubConnectionBuilder()
    .WithUrl(conn.Url, options =>
    {
        options.AccessTokenProvider = () => Task.FromResult(conn.AccessToken);
    })
    .ConfigureLogging(logging =>
    {
        logging.SetMinimumLevel(LogLevel.Debug);
    })
    .WithAutomaticReconnect()
    .Build();
    
//Define your event handlers. These need to match the Azure Function exactly. Add more event handlers for OnConnection, OnDisconnect, etc.
hubConnection.On<NewMessage>($"newMessage", newMessage =>
{
    //Do your work with the message
    StateHasChanged();
});

//Start the hub connection
await hubConnection.StartAsync();

These classes may help give you a path forward. The structure of the messages needs to match in the Function App and the Web App.

public class SignalRConnectionInfo
{
    
    [JsonPropertyName("url")]
    public string Url { get; set; }
    
    [JsonPropertyName("accessToken")]
    public string AccessToken { get; set; }

}

public class NewMessage
{
    [JsonPropertyName("connectionId")]    
    public string ConnectionId { get; set; }

    [JsonPropertyName("sender")]    
    public string Sender { get; set; }
    
    [JsonPropertyName("text")]    
    public string Text { get; set; }
}

Hopefully this helps illustrate the major steps. Good luck!

jassent
  • 529
  • 3
  • 10