В этом разделе вы узнаете, как создавать приложения для потоковой передачи и получения данных с помощью ASP.NET Core SignalR и Angular. .

Что вам понадобится:

  • Базовые знания ASP.NET Core и Angular.
  • Установлены .NET Core 3.1 и среда разработки Visual Studio 2019.

Что вы узнаете к концу этой темы:

  • Как добавить и использовать SignalR.
  • Как открыть клиентское соединение и использовать концепцию вызова метода для потоковой передачи данных для каждого клиента.
  • Как использовать службу SignalR с приложением Angular с помощью Observables.

SignalR использует преимущества нескольких транспортов и автоматически выбирает лучший доступный транспорт с учетом возможностей клиента и сервера - WebSockets, Server Send Events или Long-polling.

Когда мы говорим в терминах WebSockets (исключение SSE и Long-polling из уравнения), когда клиент подключен к серверу в режиме реального времени, всякий раз, когда что-то происходит, сервер будет знать, что нужно отправить сообщение через этот WebSocket обратно на сервер. клиент. С клиентами и серверами старой закалки будет использоваться транспорт с длительным опросом.

Вот как SignalR обрабатывает современные клиенты и серверы: он использует скрытые веб-сокеты, когда они доступны, и изящно возвращается к другим методам и технологиям, когда это не так:

Это похоже на рукопожатие: клиент и сервер соглашаются, что использовать, и используют это. Это называется согласованием процесса.

Демо SignalR

Цель этой демонстрации - продемонстрировать доску финансового экрана с потоком данных в реальном времени с использованием ASP.NET Core SignalR. Демо в полноэкранном режиме.

Конфигурация сервера SignalR

Создать приложение ASP.NET Core

Давайте посмотрим, как настроить приложение ASP.NET Core SignalR. В Visual Studio из Файл ›› Новый проект выберите веб-приложение ASP.NET Core и следите за настройкой. Не стесняйтесь следовать официальному руководству по документации Microsoft, если у вас возникнут какие-либо трудности с настройкой.

Настройка конфигурации SignalR

Добавьте в файл Startup.cs следующий код:

  • Конечная часть метода Configure.
app.UseEndpoints(endpoints => 
{     
   endpoints.MapControllers();
   endpoints.MapHub<StreamHub>("/streamHub");
});
  • Добавьте использование SignalR в метод ConfigureServices.
services.AddSignalR(options => 
{
   options.EnableDetailedErrors = true;
});

Приведенные выше изменения добавляют SignalR в систему внедрения и маршрутизации зависимостей ASP.NET Core.

Теперь давайте настроим дополнительную базовую конфигурацию. Откройте файл properties / launchSettings.json и измените его соответствующим образом:

"profiles": { 
   "WebAPI": { 
     "commandName": "Project", 
     "launchBrowser": false, 
     "applicationUrl": "https://localhost:5001; http://localhost:5000", 
      "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" 
     } 
   } 
}

Наш серверный проект будет работать на localhost: 5001, а клиентский - на localhost: 4200, поэтому для установления связи между ними нам нужно включить CORS. Откроем класс Startup.cs и изменим его:

public void ConfigureServices(IServiceCollection services) 
{ 
   services.AddCors(options => 
   { 
      options.AddPolicy("CorsPolicy", builder => builder
      .AllowAnyMethod()
      .AllowAnyHeader()
      .AllowCredentials()
      .WithOrigins("http://localhost:4200"));
   }); 
   ... 
   public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 
   { 
      ... 
      app.UseCors("CorsPolicy"); 
      ...

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

Настройка SignalR Hub

Начнем с объяснения, что такое концентратор SignalR? API-интерфейс SignalR Hub позволяет вызывать методы на подключенных клиентах с сервера. В коде сервера вы определяете методы, вызываемые клиентом. В SignalR есть концепция под названием Вызов - на самом деле вы можете вызывать концентратор от клиента с помощью определенного метода. В клиентском коде вы определяете методы, вызываемые с сервера.

Фактический концентратор находится на стороне сервера. Представьте, что у вас есть клиенты и хаб находится между ними всеми. Вы можете сказать что-то всем клиентам с помощью Clients.All.doWork(), вызвав метод на концентраторе. Это касается всех подключенных клиентов. Кроме того, вы можете общаться только с одним клиентом, которым является вызывающий, потому что он является вызывающим для этого конкретного метода.

Мы создали класс StreamHub, который наследует базовый класс Hub, который отвечает за управление подключениями, группами и обменом сообщениями. Следует иметь в виду, что класс Hub не имеет состояния, и каждый новый вызов определенного метода происходит в новом экземпляре этого класса. Бесполезно сохранять состояние в свойствах экземпляра, скорее мы предлагаем использовать статические свойства, в нашем случае мы используем статическую коллекцию пар ключ-значение для хранения данных для каждого подключенного клиента.

Другие полезные свойства этого класса: Клиенты, Контекст, и Группы. Они могут помочь вам управлять определенным поведением на основе уникального ConnectionID. Кроме того, этот класс предоставляет вам следующие полезные методы:

  • OnConnectedAsync () - вызывается при установке нового соединения с концентратором.
  • OnDisconnectedAsync (Exception) - вызывается при разрыве соединения с концентратором.

Они позволяют нам выполнять любую дополнительную логику при установке или закрытии соединения. В наше приложение мы также добавили метод UpdateParameters, который получает идентификатор подключения контекста и использует его для отправки данных через определенный интервал. Как видите, мы общаемся через уникальный ConnectionID, который предотвращает вмешательство потоковой передачи со стороны других клиентов.

public async void UpdateParameters(int interval, int volume, bool live = false, bool updateAll = true) 
{ 
   ... 
   var connection = Context.ConnectionId; var clients = Clients; 
   ... 
   if (!clientConnections.ContainsKey(connection)) 
   { 
      clientConnections.Add(connection, new TimerManager(async() => 
      { 
         ... 
         Send(newDataArray, client, connection); 
      }, interval)); 
   } else 
   { 
      clientConnections[connection].Stop(); 
      clientConnections[connection] = new TimerManager(async () => 
      { 
         var client = clients.Client(connection); 
         ...
         await Send(newDataArray, client, connection); 
      }, interval); 
   }
   ... 
}

Когда данные готовы, мы передаем их, генерируя событие transferdata с помощью метода SendAsync.

public async Task Send(FinancialData[] array, IClientProxy client, string connection) 
{ 
   await client.SendAsync("transferdata", array); 
} 
... 
// Called when a connection with the hub is terminated public override Task OnDisconnectedAsync(Exception exception) 
{ 
   StopTimer(); 
   clientConnections.Remove(Context.ConnectionId); 
   return base.OnDisconnectedAsync(exception); 
}

Наше клиентское приложение будет прослушивать зарегистрированные события:

private registerSignalEvents() 
{ 
   this.hubConnection.onclose(() => { 
      this.hasRemoteConnection = false; 
   }); 
   this.hubConnection.on('transferdata', (data) => { 
      this.data.next(data); 
   });
};

Публичный репозиторий GitHub Приложение ASP.NET Core можно найти здесь.

Создать клиентскую библиотеку SignalR

Мы создадим проект Angular, чтобы использовать сервис SignalR. Репозиторий Github с актуальным приложением можно найти здесь.

Сначала начните с установки SignalR:

npm install @microsoft/signalr

Имейте в виду, что мы собираемся отправить HTTP-запрос на наш сервер, поэтому нам также понадобится HttpClientModule.

Ниже вы найдете файл signal-r.service.ts, который обрабатывает построитель соединений с концентратором.

export class SignalRService implements OnDestroy {
    public data: BehaviorSubject<any[]>;
    public hasRemoteConnection: boolean;
    private hubConnection: signalR.HubConnection;
    ...
    constructor(private zone: NgZone, private http: HttpClient) {
        this.data = new BehaviorSubject([]);
    }
    ...
    // Start Hub Connection and Register events
    public startConnection = (interval = 500, volume = 1000, live = false,  updateAll = true) => {
        this.hubConnection = new signalR.HubConnectionBuilder()
            .configureLogging(signalR.LogLevel.Trace)
            .withUrl('https://www.infragistics.com/angular-apis/webapi/streamHub')
            .build();
        this.hubConnection
            .start()
            .then(() => {
                ...
                this.registerSignalEvents();
                this.broadcastParams(interval, volume, live, updateAll);
            })
            .catch(() => { ... });
    }
    // Change the parameters like frequency and data volume
    public broadcastParams = (frequency, volume, live, updateAll = true) => {
        this.hubConnection.invoke('updateparameters', frequency, volume, live, updateAll)
            .then(() => console.log('requestLiveData', volume))
            .catch(err => {
                console.error(err);
            });
    }
    // Register events
    private registerSignalEvents() {
        this.hubConnection.onclose(() => {
            this.hasRemoteConnection = false;
        });
        this.hubConnection.on('transferdata', (data) => {
            this.data.next(data);
        })
    }

В добавлении app.component используйте только что созданный метод startConnection

constructor(public dataService: SignalRService) {} 
public ngOnInit() { 
   this.dataService.startConnection(this.frequency, this.dataVolume, 
   true, false); 
} 

Привязка данных сетки

Как мы уже видели в нашем клиентском коде, мы настроили прослушиватель для события transferdata, которое получает в качестве аргумента обновленный массив данных. Чтобы передать только что полученные данные в нашу сетку, мы используем наблюдаемый. Чтобы установить это, нам нужно привязать источник данных сетки к наблюдаемым данным следующим образом:

Каждый раз, когда от сервера к клиенту приходят новые данные, мы вызываем next() метод наблюдаемых данных.

this.hubConnection.on('transferdata', (data) => 
{ 
   this.data.next(data); 
});

Выводы по теме

Если вы не хотите обновлять свое приложение, а просто смотрите, когда обновляются данные, вам следует подумать о ASP.NET Core SignalR. Я определенно рекомендую использовать потоковый контент, когда вы думаете, что ваши данные велики, или если вы хотите, чтобы пользовательский интерфейс был гладким, не блокируя клиента, показывая бесконечные счетчики.

Использование SignalR Hub для связи является простым и интуитивно понятным, а с помощью Angular Observables вы можете создать мощное приложение, использующее потоковую передачу данных с помощью WebSockets.

Первоначально опубликовано на https://www.infragistics.com.