Client SignalR .NET se connectant au service Azure SignalR dans une application Blazor .NET Core 3

11

J'essaie d'établir une connexion entre mon application ASP.NET Core 3.0 Blazor (côté serveur) et le service Azure SignalR. Je finirai par injecter mon client SignalR (service) dans quelques composants Blazor afin qu'ils mettent à jour mon interface utilisateur / DOM en temps réel.

Mon problème est que je reçois le message suivant lorsque j'appelle ma .StartAsync()méthode sur la connexion concentrateur:

Le code d'état de réponse n'indique pas la réussite: 404 (introuvable).

BootstrapSignalRClient.cs

Ce fichier charge ma configuration pour le service SignalR, y compris l'URL, la chaîne de connexion, la clé, le nom de la méthode et le nom du concentrateur. Ces paramètres sont capturés dans la classe statique SignalRServiceConfigurationet utilisés ultérieurement.

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);
    }
}

J'ai de l'expérience en utilisant SignalR avec .NET Core où vous l'ajoutez, de sorte que le Startup.csfichier utilisant .AddSignalR().AddAzureSignalR()et mappant un concentrateur dans la configuration de l'application et le faire de cette manière nécessite l'établissement de certains paramètres de «configuration» (c'est-à-dire la chaîne de connexion).

Compte tenu de ma situation, où HubConnectionBuilderobtient la chaîne de connexion ou une clé pour s'authentifier auprès du service SignalR?

Est-il possible que le message 404 soit le résultat de la clé / chaîne de connexion manquante?

Jason Shave
la source
1
.WithUrl(signalRConfig.Url + signalRConfig.HubName)Pouvez-vous vérifier que cela aboutit à l'URL correcte? (Par point d'arrêt ou enregistrement?)
Fildor
J'ai trouvé utile d'avoir l'Uri de base comme Uriet de construire l'intégralité via Uri (Uri, chaîne)
Fildor
ce qui est intéressant, c'était un "hareng rouge" et n'avait rien à voir avec le 404.
Jason Shave

Réponses:

8

D'accord, il s'avère que la documentation manque ici d'informations clés. Si vous utilisez le client .NET SignalR pour vous connecter au service Azure SignalR, vous devez demander un jeton JWT et le présenter lors de la création de la connexion concentrateur.

Si vous devez vous authentifier au nom d'un utilisateur, vous pouvez utiliser cet exemple.

Sinon, vous pouvez configurer un point de terminaison "/ négocier" à l'aide d'une API Web telle qu'une fonction Azure pour récupérer un jeton JWT et une URL client pour vous; c'est ce que j'ai fini par faire pour mon cas d'utilisation. Des informations sur la création d'une fonction Azure pour obtenir votre jeton JWT et votre URL peuvent être trouvées ici.

J'ai créé une classe pour contenir ces deux valeurs en tant que telles:

SignalRConnectionInfo.cs

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

J'ai également créé une méthode à l'intérieur de ma SignalRServicepour gérer l'interaction avec le point de terminaison «/ négocier» de l'API Web dans Azure, l'instanciation de la connexion concentrateur et l'utilisation d'un événement + délégué pour recevoir des messages comme suit:

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();
}

Le functionsClientest simplement un HttpClientpré-configuré fortement typé avec une URL de base et le FunctionsClientConstants.SignalRest une classe statique avec le chemin "/ négociation" qui est ajouté à l'URL de base.

Une fois que j'ai tout installé, j'ai appelé le await hubConnection.StartAsync();et il "s'est connecté"!

Après tout cela, j'ai mis en place un ReceiveMessageévénement statique et un délégué comme suit (dans le même SignalRClient.cs):

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

Enfin, j'ai implémenté le ReceiveMessagedélégué:

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)
}

J'ai fourni des mises à jour de la documentation à l'équipe du service Azure SignalR et j'espère que cela aidera quelqu'un d'autre!

Jason Shave
la source