Getting started with SignalR using ASP.NET Core and Angular

This article shows how to setup a first SignalR Hub in ASP.NET Core 2.2 and use it with an Angular client. SignalR was released with dotnet 2.1. Thanks to Dennis Alberti for his help in setting up the code example.

Code: https://github.com/damienbod/AspNetCoreAngularSignalR

Posts in this series

History

2023-01-08 Updated Angular 15, .NET 7
2021-01-25 Updated Angular 11.1.0 .NET 5, ngrx implementation
2020-03-21 updated packages, fixed Admin UI STS
2019-08-18 Updated ASP.NET Core 3.0, Angular 8.2.2
2019-02-06 Updated Angular 7.2.4, latest NGRX, SignalR CORS fix
2018-12-12 Updated .NET Core 2.2, ASP.NET Core SignalR 1.1.0, Angular 7.1.3
2018-05-31 Updated Microsoft.AspNetCore.SignalR 2.1
2018-05-08 Updated Microsoft.AspNetCore.SignalR 2.1 rc1, Angular 6
2018-03-15 Updated signalr Microsoft.AspNetCore.SignalR 1.0.0-preview1-final, Angular 5.2.8, @aspnet/signalr 1.0.0-preview1-update1

The required SignalR Nuget packages and npm packages are at present hosted on MyGet. Your need to add the SignalR packagesto the csproj file. To use the MyGet feed, add the https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json to your package sources.

<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.1" PrivateAssets="all">
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Remotion.Linq" Version="2.2.0" />

Now create a simple default hub.

using Microsoft.AspNetCore.SignalR;

namespace AspNetCoreAngularSignalR.SignalRHubs;

public class LoopyHub : Hub
{
    public Task Send(string data)
    {
        return Clients.All.SendAsync("Send", data);
    }
}

Add the SignalR configuration in the startup class. The hub which was created before needs to be added in the UseSignalR extension method.


using AspNetCoreAngularSignalR.Providers;
using AspNetCoreAngularSignalR;
using Microsoft.EntityFrameworkCore;
using AspNetCoreAngularSignalR.SignalRHubs;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.AddServerHeader = false;
});

var services = builder.Services;
var configuration = builder.Configuration;
var env = builder.Environment;

...

services.AddCors(options =>
{
    options.AddPolicy("AllowAllOrigins",
        builder =>
        {
            builder
                .AllowCredentials()
                .WithOrigins(
                    "https://localhost:4200")
                .SetIsOriginAllowedToAllowWildcardSubdomains()
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
});

services.AddSignalR()
  .AddMessagePackProtocol();

var app = builder.Build();

// IdentityModelEventSource.ShowPII = true;

app.UseCors("AllowAllOrigins");

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseRouting();

app.MapHub<LoopyHub>("/loopy");
app.MapHub<NewsHub>("/looney");
app.MapHub<LoopyMessageHub>("/loopymessage");
app.MapHub<ImagesMessageHub>("/zub");

...

app.Run();

Setup the Angular application. The Angular application is setup using a wepback build and all dependencies are added to the packages.json file.

Now add the required SignalR npm packages to the packages.json file. Using the npm package from NuGet:

"dependencies": {
	"@angular/animations": "^15.0.4",
	"@angular/common": "^15.0.4",
	"@angular/compiler": "^15.0.4",
	"@angular/core": "^15.0.4",
	"@angular/forms": "^15.0.4",
	"@angular/platform-browser": "^15.0.4",
	"@angular/platform-browser-dynamic": "^15.0.4",
	"@angular/router": "^15.0.4",
	"@microsoft/signalr": "7.0.0",
	"@ngrx/effects": "15.1.0",
	"@ngrx/entity": "15.1.0",
	"@ngrx/router-store": "15.1.0",
	"@ngrx/store": "15.1.0",
	"@ngrx/store-devtools": "15.1.0",
	"angular-auth-oidc-client": "15.0.2",
	"bootstrap": "^4.6.0",
	"jquery": "^3.6.0",
	"msgpack5": "5.2.1",
	"popper.js": "^1.16.1",
	"rxjs": "~6.6.6",
	"save-dev": "^0.0.1-security",
	"tslib": "^2.1.0",
	"zone.js": "~0.11.4"
},

Add the SignalR client code. In this basic example, it is just added directly in a component. The sendMessage funtion sends messages and the hubConnection.on function receives all messages including its own.

import { Component, OnInit } from '@angular/core';
import { HubConnection } from '@microsoft/signalr';
import { Configuration } from '../../app.constants';
import { OidcSecurityService } from 'angular-auth-oidc-client';
import * as signalR from '@microsoft/signalr';

@Component({
  selector: 'app-home-component',
  templateUrl: './home.component.html',
})
export class HomeComponent implements OnInit {
  private hubConnection: HubConnection | undefined;
  async: any;
  message = '';
  messages: string[] = [];

  isAuthenticated = false;

  constructor(
    private configuration: Configuration,
    private oidcSecurityService: OidcSecurityService
  ) {}

  ngOnInit() {
    this.oidcSecurityService.isAuthenticated$.subscribe(
      ({isAuthenticated}) => {
        this.isAuthenticated = isAuthenticated;
        if (isAuthenticated) {
          this.init();
        }
      }
    );
  }

  sendMessage(): void {
    const data = `Sent: ${this.message}`;
    if (this.hubConnection) {
      this.hubConnection.invoke('Send', data);
    }
    this.messages.push(data);
  }

  private init() {
    let tokenValue = '';
    this.oidcSecurityService.getAccessToken().subscribe((token) => {
      // console.log(token)
      if (token !== '') {
        tokenValue = '?token=' + token;
      }
    });

    this.hubConnection = new signalR.HubConnectionBuilder()
      .withUrl(`${this.configuration.Server}signalrhome${tokenValue}`)
      .configureLogging(signalR.LogLevel.Information)
      .build();

    this.hubConnection.start().catch((err) => console.error(err.toString()));

    this.hubConnection.on('Send', (data: any) => {
      const received = `Received: ${data}`;
      this.messages.push(received);
    });
  }
}

The messages are then displayed in the component template.

<div class="container-fluid" *ngIf="isAuthenticated">
  <form (ngSubmit)="sendMessage()" #messageForm="ngForm">
    <div class="form-group row">
      <label for="message" class="col-sm-2 col-form-label">Message</label>
      <div class="col-sm-4">
        <input
          type="text"
          class="form-control"
          id="message"
          placeholder="your message..."
          name="message"
          [(ngModel)]="message"
          required
        />
      </div>
      <div class="col-sm-2">
        <button
          type="submit"
          class="btn btn-primary"
          [disabled]="!messageForm.valid"
        >
          Send SignalR Message
        </button>
      </div>
    </div>
  </form>

  <div class="row" *ngIf="messages.length > 0">
    <div class="table-responsive">
      <table class="table">
        <thead>
          <tr>
            <th>#</th>
            <th>Messages</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let message of messages; let i = index">
            <td>{{ i + 1 }}</td>
            <td>{{ message }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
  <div class="row" *ngIf="messages.length <= 0">
    <span>No messages</span>
  </div>
</div>

Now the first really simple SignalR Hub is setup and an Angular client can send and receive messages.

Links:

https://learn.microsoft.com/en-us/aspnet/core/signalr/introduction

https://www.npmjs.com/package/@aspnet/signalr-client

https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json

https://dotnet.myget.org/F/aspnetcore-ci-dev/npm/

https://dotnet.myget.org/feed/aspnetcore-ci-dev/package/npm/@aspnet/signalr-client

https://www.npmjs.com/package/msgpack5

36 comments

  1. […] Getting started with SignalR using ASP.NET Core and Angular (Damien Bowden) […]

  2. Nice and simple write up thanks. Would be great to see one similar for React. Anyone?

  3. […] Getting started with SignalR using ASP.NET Core and Angular – Damien Bowden […]

  4. hi damienbod
    Would you like to write asp.net core 2 binding collections with non-sequential indices, add an item and remote an item in razor page?

    1. sounds interesting, greetings Damien

  5. Elibariki Muna · · Reply

    I am using systemjs and would like to see the configuration for this to work

    1. +1. I also use systemjs.

  6. Branislav Petrovic · · Reply

    Hi Damien,

    I tested this sample with dev and prod webpack configuration. Both applications work fine in Chrome, Firefox and Opera, but doesn’t work in Safari-desktop (SyntaxError: Unexpected token ‘const’) and IE11 (Syntax error, class keyword is problem in syntax). I figured out that this is ES6 problem, because latter browsers doesn’t support some ES6 features.

    I include Babel in webpack configuration with transform-runtime plugin and es2015 preset, and this doesn’t transpile problematic ES6 code to ES5. I’ll be grateful if you have any tip on this issue.

    BR,
    Branislav

    1. Hi Branislav, thanks for this info, I never tested in Safari, IE11 should work. I test this and let you know what if find, (As soon as I get time, very busy with the day job)

      Greetings Damien

  7. […] For more information here […]

  8. In the demo it says @angular/platform-server unavailable 😦

    1. npm install, npm run build-production, does this work?

      Greetings Damien

  9. i am getting error like Transports.js:28 WebSocket connection to ‘ws://localhost:51383/chat?id=95976d32-4361-411e-aa5f-cca73f352cef’ failed: Error during WebSocket handshake: Unexpected response code: 204

    1. If you are running the SignalR application on Windows 7 using IIS Express, try running the application using Kestrel instead.

  10. I keep getting Error: Failed to start the connection. SyntaxError: Unexpected token < in JSON at position 0

  11. I’m getting this error: Failed to load http://localhost:49888/signalr: Method OPTIONS is not allowed by Access-Control-Allow-Methods in preflight response.
    I have already enabled all sorts of CORS and none fix the issue.

    1. Are the options request disabled on the Web server?

  12. Guess Whover · · Reply

    Can SignalR be used to update data that’s already on the screen? E.g. user A is looking at a list of two products and their prices (Product A: $1, Product B: $2). User B updates the price of Product A to $3. Is there a way to push that update to all users, so that they see Product A: $3 ?

  13. […] Getting started with SignalR using ASP.NET Core and Angular […]

  14. Kaushik · · Reply

    Good blog, In production server getting an error “, cannot send data if the connection is not in the ‘Connected’ State”

    Connection make successfully, but after sometimes the connection is closed and it generates above error

  15. hello , i was following your steps but ,i have faced this error :
    “Error: Failed to start the connection: Error: Unable to initialize any of the available transports.”
    any ideas?

    1. nsteuver · · Reply

      Try updating to the newest version of the Angular client signalR package as well as the .NET Core signalR Nuget package. I ran into this error and found out I had the newest version of the client but not the .NET package.

  16. John · · Reply

    Apologies for very newbie question.
    Have compiled and run the app, get the indexpage displaying “Loading….”. Cannot see how to access the home page or any of the other news pages. All of the code looks correct compared to the examples above.
    In order to get my version running I needed to change the directory name of angularApp to wwwroot. I then also changed any other path reference to wwwroot.

  17. Hi,

    There is 2 times “this._hubConnection.start()” in ngInit, is it normal?

    Thank for your article,
    Cedric

    1. Hi Cédric, thanks, I’ll fix this, thanks for reporting,
      Greetings Damien

  18. Hi thanks for your guide, I got this error: Error: StaticInjectorError(AppModule)[HubConnection]:
    StaticInjectorError(Platform: core)[HubConnection]:
    NullInjectorError: No provider for HubConnection!

  19. Thanks.

    This should work, are you using correct signalR npm?

    the https://github.com/damienbod/AspNetCoreAngularSignalR/blob/master/src/AspNetCoreAngularSignalR/package.json#L29

    Maybe you could compare you’re code to the code on github?

    greetings Damien

  20. jahangir · · Reply

    I following your code but get error Connection ID Required

  21. Maybe the cert is not allowed in your browser. Allow the iisexpress cert. If this does not work, compare you code to the github:

    https://github.com/damienbod/AspNetCoreAngularSignalR/tree/master/src/AspNetCoreAngularSignalR

    Hope this helps
    Greetings Damien

  22. jahangir · · Reply

    public class ChatHub : Hub
    {
    public Task Send(string data)
    {
    return Clients.All.SendAsync(“Send”, data);
    }

    }
    /////////////////////// startup.cs/////////
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
    services.AddCors(options =>
    {
    options.AddPolicy(“CorsPolicy”,
    builder => builder.WithOrigins(origions)
    .AllowAnyMethod()
    .AllowAnyHeader()
    .AllowCredentials()
    );
    });
    services.Configure(options =>
    {
    options.Filters.Add(new CorsAuthorizationFilterFactory(“CorsPolicy”));

    });
    services.AddSignalR();
    services.AddSingleton();
    //services.AddLogging(builder => builder.AddLog4Net(“log4net.config”));
    return ConfigureIoC(services);
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
    app.UseAuthentication();
    app.UseCors(“CorsPolicy”);
    app.UseSignalR(routes =>
    {
    routes.MapHub(“/Notification”);
    });
    app.UseSignalR(routes =>
    {
    routes.MapHub(“/chat”);
    });

    app.UseMvc();
    }
    //////////////////////////////////////////////// Angular Code/////////////////////////////////
    import { ISubscription } from “rxjs/Subscription”;
    import { Component, OnInit, OnDestroy } from “@angular/core”;

    import { AuthService } from ‘../../auth/auth.service’;
    import { SidebarService } from ‘../sidebar/sidebar.service’
    import { fadeInContent } from ‘@angular/material’;
    import { NotificationsService } from ‘../../services/notifications.service’;
    import { AppConfig } from ‘../../app.config’;
    import { HubConnection } from ‘@aspnet/signalr’;
    import * as signalR from ‘@aspnet/signalr’;
    @Component({
    selector: ‘app-navbar’,
    templateUrl: ‘./navbar.component.html’,
    styleUrls: [‘./navbar.component.scss’]
    })

    export class NavbarComponent implements OnInit, OnDestroy {

    public internal: any = [];
    public RoleList: any = [];
    public count_record: any =[]
    constructor(public authService: AuthService, private config: AppConfig, public sideBarService: SidebarService, public notiService: NotificationsService) {
    this.authService.GetUserInfo();
    this.RoleList = this.authService.RoleList;
    // this.LoadNotification();

    }

    public LoadNotification() {
    this.notiService.getNotification();

    }
    public async: any;
    message = ”;
    messages: string[] = [];
    private _hubConnection: HubConnection | undefined;
    public sendMessage(): void {
    const data = ‘Sent: ${this.message}’;

    if (this._hubConnection) {
    this._hubConnection.invoke(‘Send’, data);
    }
    this.messages.push(data);
    }

    ngOnInit() {

    this._hubConnection = new signalR.HubConnectionBuilder()
    .withUrl(this.config.getBaseUrl() + “chat”)
    .configureLogging(signalR.LogLevel.Information)
    .build();

    this._hubConnection.start().catch(err => {
    debugger;
    console.error(err.toString())
    });

    this._hubConnection.on(‘Send’, (data: any) => {
    const received = `Received: ${data}`;

    });

    }
    ngOnDestroy(): void {

    }
    }

    Please help me to identify the issue

  23. jahangir · · Reply

    I’m using HTTP

  24. jahangir · · Reply

    Now I get the Error :WebSocket is not in the OPEN state
    (anonymous) @ navbar.component.ts:57
    2zone.js:388 Uncaught (in promise) WebSocket is not in the OPEN state

    1. vuqluskr · · Reply

      getting same error, did you manage to fix it?

  25. […] Getting started with SignalR using ASP.NET Core and Angular […]

  26. Philip Ayaviri A · · Reply

    Hi, I have a question, where is it that in angular listening to only one group?

  27. venkatesh parihar · · Reply

    Can we pass a dynamic array URL in hub connections if yes then how we can stop individual hub connections

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.