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
- Getting started with SignalR using ASP.NET Core and Angular
- SignalR Group messages with ngrx and Angular
- Using EF Core and SQLite to persist SignalR Group messages in ASP.NET Core
- Securing an Angular SignalR client using JWT tokens with ASP.NET Core and IdentityServer4
- Implementing custom policies in ASP.NET Core using the HttpContext
- Sending Direct Messages using SignalR with ASP.NET core and Angular
- Using Message Pack with ASP.NET Core SignalR
- Uploading and sending image messages with ASP.NET Core SignalR
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
[…] Getting started with SignalR using ASP.NET Core and Angular (Damien Bowden) […]
Nice and simple write up thanks. Would be great to see one similar for React. Anyone?
[…] Getting started with SignalR using ASP.NET Core and Angular – Damien Bowden […]
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?
sounds interesting, greetings Damien
I am using systemjs and would like to see the configuration for this to work
+1. I also use systemjs.
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
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
[…] For more information here […]
In the demo it says @angular/platform-server unavailable 😦
npm install, npm run build-production, does this work?
Greetings Damien
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
If you are running the SignalR application on Windows 7 using IIS Express, try running the application using Kestrel instead.
I keep getting Error: Failed to start the connection. SyntaxError: Unexpected token < in JSON at position 0
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.
Are the options request disabled on the Web server?
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 ?
[…] Getting started with SignalR using ASP.NET Core and Angular […]
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
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?
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.
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.
Hi,
There is 2 times “this._hubConnection.start()” in ngInit, is it normal?
Thank for your article,
Cedric
Hi Cédric, thanks, I’ll fix this, thanks for reporting,
Greetings Damien
Hi thanks for your guide, I got this error: Error: StaticInjectorError(AppModule)[HubConnection]:
StaticInjectorError(Platform: core)[HubConnection]:
NullInjectorError: No provider for HubConnection!
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
I following your code but get error Connection ID Required
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
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
I’m using HTTP
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
getting same error, did you manage to fix it?
[…] Getting started with SignalR using ASP.NET Core and Angular […]
Hi, I have a question, where is it that in angular listening to only one group?
Can we pass a dynamic array URL in hub connections if yes then how we can stop individual hub connections