SignalR .NET-Client, der in einer Blazor .NET Core 3-Anwendung eine Verbindung zum Azure SignalR-Dienst herstellt

11

Ich versuche, eine Verbindung zwischen meiner ASP.NET Core 3.0 Blazor-Anwendung (serverseitig) und dem Azure SignalR-Dienst herzustellen. Am Ende injiziere ich meinen SignalR-Client (Service) in einige Blazor-Komponenten, damit diese meine Benutzeroberfläche / mein DOM in Echtzeit aktualisieren.

Mein Problem ist, dass ich die folgende Nachricht erhalte, wenn ich meine .StartAsync()Methode über die Hub-Verbindung aufrufe:

Der Antwortstatuscode zeigt keinen Erfolg an: 404 (Nicht gefunden).

BootstrapSignalRClient.cs

Diese Datei lädt meine Konfiguration für den SignalR-Dienst, einschließlich URL, Verbindungszeichenfolge, Schlüssel, Methodenname und Hubname. Diese Einstellungen werden in der statischen Klasse erfasst SignalRServiceConfigurationund später verwendet.

public static class BootstrapSignalRClient
{
    public static IServiceCollection AddSignalRServiceClient(this IServiceCollection services, IConfiguration configuration)
    {
        SignalRServiceConfiguration signalRServiceConfiguration = new SignalRServiceConfiguration();
        configuration.Bind(nameof(SignalRServiceConfiguration), signalRServiceConfiguration);

        services.AddSingleton(signalRServiceConfiguration);
        services.AddSingleton<ISignalRClient, SignalRClient>();

        return services;
    }
}

SignalRServiceConfiguration.cs

public class SignalRServiceConfiguration
{
    public string ConnectionString { get; set; }
    public string Url { get; set; }
    public string MethodName { get; set; }
    public string Key { get; set; }
    public string HubName { get; set; }
}

SignalRClient.cs

public class SignalRClient : ISignalRClient
{
    public delegate void ReceiveMessage(string message);
    public event ReceiveMessage ReceiveMessageEvent;

    private HubConnection hubConnection;

    public SignalRClient(SignalRServiceConfiguration signalRConfig)
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(signalRConfig.Url + signalRConfig.HubName)
            .Build();            
    }

    public async Task<string> StartListening(string id)
    {
        // Register listener for a specific id
        hubConnection.On<string>(id, (message) => 
        {
            if (ReceiveMessageEvent != null)
            {
                ReceiveMessageEvent.Invoke(message);
            }
        });

        try
        {
            // Start the SignalR Service connection
            await hubConnection.StartAsync(); //<---I get an exception here
            return hubConnection.State.ToString();
        }
        catch (Exception ex)
        {
            return ex.Message;
        }            
    }

    private void ReceiveMessage(string message)
    {
        response = JsonConvert.DeserializeObject<dynamic>(message);
    }
}

Ich habe Erfahrung mit der Verwendung von SignalR mit .NET Core, wo Sie es hinzufügen, sodass für die Startup.csDatei, die .AddSignalR().AddAzureSignalR()einen Hub in der App-Konfiguration verwendet und zuordnet , bestimmte Konfigurationsparameter eingerichtet werden müssen (z. B. Verbindungszeichenfolge).

Woher kommt in meiner Situation HubConnectionBuilderdie Verbindungszeichenfolge oder ein Schlüssel zur Authentifizierung beim SignalR-Dienst?

Ist es möglich, dass die Nachricht 404 ein Ergebnis des fehlenden Schlüssels / der fehlenden Verbindungszeichenfolge ist?

Jason Shave
quelle
1
.WithUrl(signalRConfig.Url + signalRConfig.HubName)Können Sie überprüfen, ob dies zur richtigen URL führt? (Durch Haltepunkt oder Protokollierung?)
Fildor
Ich fand es nützlich, die Basis Uri als zu haben Uriund die vollständige über Uri (Uri, String) zu
konstruieren
Interessanterweise war es ein "roter Hering" und hatte nichts mit dem 404 zu tun.
Jason Shave

Antworten:

8

Okay, es stellt sich heraus, dass der Dokumentation hier eine wichtige Information fehlt. Wenn Sie den .NET SignalR-Client verwenden, der eine Verbindung zum Azure SignalR-Dienst herstellt, müssen Sie ein JWT-Token anfordern und beim Erstellen der Hub-Verbindung vorlegen.

Wenn Sie sich im Namen eines Benutzers authentifizieren müssen, können Sie dieses Beispiel verwenden.

Andernfalls können Sie einen "/ Verhandlungs" -Endpunkt mithilfe einer Web-API wie einer Azure-Funktion einrichten, um ein JWT-Token und eine Client-URL für Sie abzurufen. Das habe ich letztendlich für meinen Anwendungsfall getan. Informationen zum Erstellen einer Azure-Funktion zum Abrufen Ihres JWT-Tokens und Ihrer URL finden Sie hier.

Ich habe eine Klasse erstellt, die diese beiden Werte als solche enthält:

SignalRConnectionInfo.cs

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

Ich habe auch eine Methode in meinem erstellt SignalRService, um die Interaktion mit dem Endpunkt "/ Verhandlung" der Web-API in Azure, die Instanziierung der Hub-Verbindung und die Verwendung eines Ereignisses + Delegaten zum Empfangen von Nachrichten wie folgt zu handhaben:

SignalRClient.cs

public async Task InitializeAsync()
{
    SignalRConnectionInfo signalRConnectionInfo;
    signalRConnectionInfo = await functionsClient.GetDataAsync<SignalRConnectionInfo>(FunctionsClientConstants.SignalR);

    hubConnection = new HubConnectionBuilder()
        .WithUrl(signalRConnectionInfo.Url, options =>
        {
           options.AccessTokenProvider = () => Task.FromResult(signalRConnectionInfo.AccessToken);
        })
        .Build();
}

Dies functionsClientist einfach eine stark typisierte HttpClientVorkonfiguration mit einer Basis-URL und FunctionsClientConstants.SignalReine statische Klasse mit dem Pfad "/ Verhandeln", der an die Basis-URL angehängt wird.

Nachdem ich alles eingerichtet hatte, rief ich das an await hubConnection.StartAsync();und es "verbunden"!

Nach all dem habe ich ein statisches ReceiveMessageEreignis und einen Delegaten wie folgt eingerichtet (im selben SignalRClient.cs):

public delegate void ReceiveMessage(string message);
public static event ReceiveMessage ReceiveMessageEvent;

Zuletzt habe ich den ReceiveMessageDelegierten implementiert :

await signalRClient.InitializeAsync(); //<---called from another method

private async Task StartReceiving()
{
    SignalRStatus = await signalRClient.ReceiveReservationResponse(Response.ReservationId);
    logger.LogInformation($"SignalR Status is: {SignalRStatus}");

    // Register event handler for static delegate
    SignalRClient.ReceiveMessageEvent += signalRClient_receiveMessageEvent;
}

private async void signalRClient_receiveMessageEvent(string response)
{
    logger.LogInformation($"Received SignalR mesage: {response}");
    signalRReservationResponse = JsonConvert.DeserializeObject<SignalRReservationResponse>(response);
    await InvokeAsync(StateHasChanged); //<---used by Blazor (server-side)
}

Ich habe dem Azure SignalR Service-Team Dokumentationsaktualisierungen zur Verfügung gestellt und hoffe, dass dies jemand anderem hilft!

Jason Shave
quelle