Klient SignalR .NET łączy się z usługą Azure SignalR w aplikacji Blazor .NET Core 3

11

Próbuję nawiązać połączenie między moją aplikacją ASP.NET Core 3.0 Blazor (po stronie serwera) a usługą Azure SignalR. W końcu wprowadzę mojego klienta SignalR (usługę) do kilku komponentów Blazora, aby zaktualizowali mój interfejs użytkownika / DOM w czasie rzeczywistym.

Mój problem polega na tym, że podczas wywoływania .StartAsync()metody w połączeniu z koncentratorem pojawia się następujący komunikat :

Kod statusu odpowiedzi nie oznacza sukcesu: 404 (Nie znaleziono).

BootstrapSignalRClient.cs

Ten plik ładuje moją konfigurację usługi SignalR, w tym adres URL, parametry połączenia, klucz, nazwę metody i nazwę koncentratora. Te ustawienia są przechwytywane w klasie statycznej SignalRServiceConfigurationi używane później.

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

Mam doświadczenie w korzystaniu z SignalR z .NET Core, gdzie dodajesz go, więc Startup.csplik używający .AddSignalR().AddAzureSignalR()i mapujący koncentrator w konfiguracji aplikacji i zrobienie tego w ten sposób wymaga ustalenia pewnych parametrów „konfiguracji” (tj. Ciągu połączenia).

Biorąc pod uwagę moją sytuację, skąd HubConnectionBuilderbierze się ciąg połączenia lub klucz do uwierzytelnienia w usłudze SignalR?

Czy to możliwe, że komunikat 404 jest wynikiem brakującego klucza / ciągu połączenia?

Jason Shave
źródło
1
.WithUrl(signalRConfig.Url + signalRConfig.HubName)Czy możesz sprawdzić, czy to prowadzi do poprawnego adresu URL? (Przez punkt przerwania lub logowanie?)
Fildor,
Uznałem, że użyteczne jest posiadanie podstawowego Uri as Urii zbudowanie kompletnego za pomocą Uri (Uri, string)
Fildor,
co ciekawe, był to „czerwony śledź” i nie miał nic wspólnego z 404.
Jason Shave,

Odpowiedzi:

8

Okej, więc okazuje się, że w dokumentacji brakuje tutaj kluczowych informacji. Jeśli używasz klienta .NET SignalR Client łączącego się z usługą Azure SignalR, musisz poprosić o token JWT i przedstawić go podczas tworzenia połączenia koncentratora.

Jeśli potrzebujesz uwierzytelnić w imieniu użytkownika, możesz skorzystać z tego przykładu.

W przeciwnym razie można skonfigurować punkt końcowy „/ negocjować” za pomocą internetowego interfejsu API, takiego jak funkcja Azure, aby pobrać za Ciebie token JWT i adres URL klienta; to właśnie skończyło się na moim przypadku użycia. Informacje na temat tworzenia funkcji platformy Azure w celu uzyskania tokena JWT i adresu URL można znaleźć tutaj.

Utworzyłem klasę do przechowywania tych dwóch wartości jako takich:

SignalRConnectionInfo.cs

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

Utworzyłem również metodę SignalRServiceobsługi interakcji z punktem końcowym interfejsu API sieci Web „/ negocjuj” na platformie Azure, tworzenia instancji połączenia z koncentratorem oraz użycia zdarzenia + delegata do otrzymywania wiadomości w następujący sposób:

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

Jest functionsClientto po prostu mocno wpisany, HttpClientwstępnie skonfigurowany podstawowy adres URL, a FunctionsClientConstants.SignalRjest to klasa statyczna ze ścieżką „/ negocjuj”, która jest dołączana do podstawowego adresu URL.

Kiedy już to wszystko skonfigurowałem, zadzwoniłem do await hubConnection.StartAsync();i „połączono”!

Po tym wszystkim skonfigurowałem ReceiveMessagezdarzenie statyczne i delegata w następujący sposób (w tym samym SignalRClient.cs):

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

Na koniec zaimplementowałem ReceiveMessagedelegata:

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

Dostarczyłem aktualizacje dokumentacji z powrotem do zespołu usługi Azure SignalR Service i mam nadzieję, że pomoże to komuś innemu!

Jason Shave
źródło