[Angular mosaic 5] Net Core SignalR Client (ReactiveX/rxjs, Configure Angular injector, Abstract class, Inject SirnalR service, Configure SignalR hub - forbid Negotiation, setUp Transport, URL, LogLevel, Anon or AU socket connection), Configure server Hub (MapHub endpoint, WithOrigins on CORS-policy, LogLevel, JWT AU).
- 1. My fast style (background, NAV menu, header) for my various technical sites.
- 2. Intercept and analyzing routing events (router-outlet onActivate event).
- 3. Pass parameters from one component to another (@Input, extends base page).
- 4. Simplest shared service (@Injectable, Subject, Injectable, LocalStorage, Store).
- 5. UIkit Modal Futures (uk-modal tag, Modal from routed page and from Menu, Modal Toggle, Modal Show, Bind by On, MouseOver Modal, Modal Alert, Modal Prompt, Close button).
- 6. Standard Interceptor and JWT injector, Login page, Auth service, and Base form with FormGroup.
- 7. Fill select/options (FormsModule, ngModel, ngModelChange, ngValue, http.get Json from Assets, LocalStorage)
- 8. Angular connection service (Enable/Disable Heartbeat, Heartbeat URL, Heartbeat Interval/RetryInterval)
- 9. Net Core SignalR Client configure(ReactiveX/rxjs, Configure Angular injector, Abstract class, Inject SignalR service, Configure SignalR hub - forbid Negotiation, setUp Transport, URL, LogLevel, Anon or AU socket connection).
9.1 Net Core SignalR Client configure(ReactiveX/rxjs, Configure Angular injector, Abstract class, Inject SignalR service, Configure SignalR hub - forbid Negotiation, setUp Transport, URL, LogLevel, Anon or AU socket connection).
Configuring SignalR client started from select correct package from JS repository.
Than, if we want to inject SignalR service to any Angular component we need to configure DI container (injector). In order to do this we need to create abstract class (SignalRService) and realization (PrivateSignalRService)
But for create Abstract class we need to define SignalR event (because event is complex type composed from SignalEventType and TDataShape) and define Enum type of SignalR event (on my case I start from event type CONTAINER_STARTED).
Abstract class allow us inject SignalR service to any project place and subscribe to particular event with reading data.
1: import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
2: import { Subscription } from 'rxjs';
3: import { SignalRService } from '../signalr/abstract-service';
4: import { SignalEventType } from '../signalr/signal-event-type';
5:
6: @Component({
7: selector: 'app-notifications',
8: templateUrl: './notifications.component.html',
9: styleUrls: ['./notifications.component.css']
10: })
11: export class NotificationsComponent implements OnInit, OnDestroy {
12: subscription!: Subscription;
13: constructor(
14: private signal: SignalRService
15: ) {
16: }
17: ngOnInit(): void {
18: this.subscription = this.signal.getDataStream<string>(SignalEventType.CONTAINER_STARTED).subscribe(message => {
19: console.log(message.data);
20: })
21: }
22:
23: ngOnDestroy() {
24: this.subscription.unsubscribe();
25: }
26: }
And this is injectable SignalR service realization.
In this code you can see all key future of SignalR service :
- (1) for working with NET CORE need to forbid Negotiation
- (2) we can connect to SignalR endpoint with JWT AU and without it (depends of server requirements)
- (3) we can define SignalR transport as WebSockets
- (4) we can define URL of endpoint based on Environment (or statically in my case)
- (5) you can see how to build SignalR connection Hub and setUp logLevel SignalR of connection Hub, processing error, start/stop SignalR connection.
- (6) you can see how to define events (subject), subscribe and filter it with ReactiveX/rxjs (reactive JS library)
1: import { Injectable } from '@angular/core';
2: import { HttpClient } from '@angular/common/http';
3: import { filter, Observable, Subject, tap } from 'rxjs';
4: import { SignalRService } from './abstract-service';
5: import { SignalEvent } from './signal-event';
6: import { SignalEventType } from './signal-event-type';
7: import { HttpTransportType, HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
8: import { LogLevel } from '@microsoft/signalr/dist/esm/ILogger';
9: //import { environment } from './../../environments/environment';
10:
11: @Injectable()
12: export class PrivateSignalRService extends SignalRService {
13:
14: private _signalEvent: Subject<SignalEvent<any>>;
15: private _openConnection: boolean = false;
16: private _isInitializing: boolean = false;
17: private _hubConnection!: HubConnection;
18: private JWT: string = "eyJhbGciOiJIUzI1....jyuxrmWeuGqIoI";
19:
20: constructor() {
21: super();
22: this._signalEvent = new Subject<any>();
23: this._isInitializing = true;
24: this._initializeSignalR();
25:
26: }
27:
28: getDataStream<TDataShape>(...filterValues: SignalEventType[]): Observable<SignalEvent<TDataShape>> {
29: this._ensureConnection();
30: return this._signalEvent.asObservable().pipe(filter(event => filterValues.some(f => f === event.type)));
31: }
32:
33: private _ensureConnection() {
34: if (this._openConnection || this._isInitializing) return;
35: this._initializeSignalR();
36: }
37:
38: private _initializeSignalR() {
39: this._hubConnection = new HubConnectionBuilder()
40: .withUrl('http://localhost:7000/DockerEventsHub',
41: {
42: withCredentials: true,
43: accessTokenFactory: () => this.JWT,
44: skipNegotiation: true,
45: transport: HttpTransportType.WebSockets
46: })
47: .configureLogging(LogLevel.Debug)
48: .build();
49: this._hubConnection.start()
50: .then(_ => {
51: this._openConnection = true;
52: this._isInitializing = false;
53: this._setupSignalREvents()
54: })
55: .catch(error => {
56: console.warn(error);
57: this._hubConnection.stop().then(_ => {
58: this._openConnection = false;
59: })
60: });
61:
62: }
63:
64: private _setupSignalREvents() {
65: this._hubConnection.on('ContainerStart', (data) => {
66: this._onMessage({ type: SignalEventType.CONTAINER_STARTED, data })
67: })
68: this._hubConnection.on('OTHER', (data) => {
69: const { numbers } = data;
70: this._onMessage({ type: SignalEventType.OTHER, data: numbers })
71: })
72: this._hubConnection.onclose((e) => this._openConnection = false);
73: }
74:
75: private _onMessage<TDataShape>(payload: SignalEvent<TDataShape>) {
76: this._signalEvent.next(payload);
77: }
78:
79: }
We can also see another code template for working with SignalR client
9.2 SignalR Server configure (endpoints.MapHub, UseCors.WithOrigins, Jwt Client Validating, logging.AddFilter("Microsoft.AspNetCore.SignalR"), logging.AddFilter("Microsoft.AspNetCore.Http.Connections"), Desktop tester.
Now we can go to my Server part. Firstly we can look to my Server configuration.
Configuring server is not simple and fast procedure, in my case this was be this procedure.
- Asynchronous MultiThreaded SSH engine for Web (Net Core 6, Linux) - Part 1,2 (Database and SSH client) StreamReader/ReadToEndAsync, Extension/Inherits/Overloads, BeginExecute/EndExecute/IAsyncResult/IProgress(Of T)/Async/Await/CancellationToken/Task.Run/Thread.Yield.
- Asynchronous MultiThreaded SSH engine for Web (Net Core 6, Linux) - Part 3,4 (CryptoAPI/CryptoService and Database Access). Protect password in DB and config, Task.Run for Async DB access, Expand POCO classes, Service lifetime list, Linq and Iterator/Yield functions.
- Asynchronous MultiThreaded SSH engine for Web (Net Core 6, Linux) - Part 5,6 (Crypt/Encrypt JWT Auth header, Middleware, User scoped service, custom AU attribute, custom HttpClient and Typed SignalRHub with saving ConnectionID to Singleton service).
- Asynchronous MultiThreaded SSH engine for Web (Net Core 6, Linux) - Part 7,8 (QuartzService/Jobs and Singleton CacheService). ConcurrentDictionary, Interlocked.Increment, SyncLock, Closure variable for MultiThreading, Cron Configuration.
- Asynchronous MultiThreaded SSH engine for Web (Net Core 6, Linux) - Part 9,10 (BackendAPI for NotificationController and my technique to testing this engine with xUnit).
But I want to emphasize a couple of points. As I mention early connection is possible on anonymous and with authenticated user.
And second point, Browser forbid SignalR connection if CORS policy on server configured without URL (look above to WithOrigins on CORS-policy).
And third point, we need very carefully bind server method to push data to browser and client subscription.
And finally notes. Before we connect Angular client we can check SignalR hub by more simple desktop client, you can use my for this purposes my Visual Studio project template https://marketplace.visualstudio.com/items?itemName=vb-net-com.SignalRConsoleTestApp
9.3 Magic result of correct Server and Client configured.
9.4 Common project magic.
This project use my service Monitoring docker events and common project magic is: when container in Linux machine started than browser intermediately receive container name.
|