Клиент SignalR .NET подключается к службе Azure SignalR в приложении Blazor .NET Core 3

Я пытаюсь установить соединение между моим приложением ASP.NET Core 3.0 Blazor (на стороне сервера) и службой Azure SignalR. В конечном итоге я внедряю свой клиент (службу) SignalR в несколько компонентов Blazor, чтобы они обновляли мой UI / DOM в реальном времени.

Моя проблема в том, что я получаю следующее сообщение, когда вызываю свой метод .StartAsync() в подключении к концентратору:

Код состояния ответа не указывает на успех: 404 (не найдено).

BootstrapSignalRClient.cs

Этот файл загружает мою конфигурацию для службы SignalR, включая URL-адрес, строку подключения, ключ, имя метода и имя концентратора. Эти настройки фиксируются в статическом классе SignalRServiceConfiguration и используются позже.

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

У меня есть опыт использования SignalR с .NET Core, где вы добавляете его, поэтому файл Startup.cs с использованием .AddSignalR().AddAzureSignalR() и сопоставление концентратора в конфигурации приложения, и для этого требуется установить определенные параметры конфигурации (например, строку подключения).

В моей ситуации, где HubConnectionBuilder получает строку подключения или ключ для аутентификации в службе SignalR?

Возможно ли, что сообщение 404 является результатом отсутствия ключа / строки подключения?


person Jason Shave    schedule 05.10.2019    source источник
comment
.WithUrl(signalRConfig.Url + signalRConfig.HubName) Можете ли вы убедиться, что это приводит к правильному URL-адресу? (По точке останова или ведению журнала?)   -  person Fildor    schedule 10.10.2019
comment
Я счел полезным иметь базовый Uri как Uri и построить полный через Uri (Uri, строка)   -  person Fildor    schedule 10.10.2019
comment
что интересно, это был отвлекающий маневр и не имел ничего общего с 404-м.   -  person Jason Shave    schedule 12.10.2019


Ответы (2)


Итак, оказывается, что в документации здесь отсутствует ключевая информация. Если вы используете клиент .NET SignalR для подключения к службе Azure SignalR, вам необходимо запросить токен JWT и предоставить его при создании подключения к концентратору.

Если вам необходимо пройти аутентификацию от имени пользователя, вы можете использовать этот пример.

В противном случае вы можете настроить конечную точку «/gotiate» с помощью веб-API, такого как функция Azure, чтобы получить за вас токен JWT и URL-адрес клиента; это то, что я в конечном итоге сделал для своего варианта использования. Информацию о создании функции Azure для получения вашего токена JWT и URL-адреса можно найти здесь.

Я создал класс для хранения этих двух значений как таковых:

SignalRConnectionInfo.cs

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

Я также создал метод внутри своего SignalRService для обработки взаимодействия с конечной точкой веб-API «/gotiate» в Azure, создания экземпляра подключения к концентратору и использования делегата событие + для получения сообщений следующим образом:

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

functionsClient - это просто строго типизированный HttpClient, предварительно настроенный с базовым URL-адресом, а FunctionsClientConstants.SignalR - это статический класс с путем "/gotiate", который добавляется к базовому URL-адресу.

Как только я все это настроил, я позвонил await hubConnection.StartAsync();, и он "подключился"!

После всего этого я установил статическое событие ReceiveMessage и делегата следующим образом (в том же SignalRClient.cs):

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

Наконец, я реализовал делегат ReceiveMessage:

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

Я отправил обновления документации обратно в команду службы Azure SignalR и надеюсь, что это поможет кому-то еще!

person Jason Shave    schedule 12.10.2019

Обновление: образец с бессерверным образцом устарел для SDK управления (образец). В пакете SDK управления используется сервер согласования.

person Christoph Spoerri    schedule 26.10.2020
comment
Это должен быть скорее комментарий, чем ответ - person Benjamin Zach; 27.10.2020
comment
Я знаю, но я не мог добавить это в качестве комментария - пропущены необходимые пункты :( - person Christoph Spoerri; 27.10.2020