Reputation: 131
I'm running a .NET core angular project in VScode on windows and I'm trying to get signalR to work but unfortunately things don't seem to be going my way.Before when I had my angular and .net core projects split up(web API and angular project separate) with the same code, this issue did not occur. It's when I switched to a .NET core angular project that it's now happening. Thanks in advance.
when I try to send a message to the server, I get the following error
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.AspNetCore.SignalR;
namespace ProjectConker
{
public class IMessage
{
public string timestamp;
public string message;
public string author;
public string iconLink;
}
public class ChatHub : Hub
{
public Task SendToAll(string name, IMessage messageData)
{
Console.WriteLine(messageData.message);
return Clients.All.SendAsync("ReceiveMessage", name, messageData);
}
}
}
Angular Client component
import { Component, OnInit } from '@angular/core';
import { ITag } from '../Tag/tag';
import { TagComponent } from '../Tag/tag.component';
import { Router, ActivatedRoute, ParamMap, Params } from '@angular/router';
import { switchMap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { IMessage } from './Message';
import { HubConnection, HubConnectionBuilder, HttpTransportType } from '@aspnet/signalr';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'chat',
templateUrl: "./chatComponent.component.html",
styleUrls: ['./chatComponent.component.css']
// providers: [ ChatService ]
})
export class ChatComponent implements OnInit {
hubConnection: HubConnection;
messageData: IMessage;
message: string;
//profile info
author: string;
iconLink: string;
messages: IMessage[] = [];
constructor(
private route: ActivatedRoute,
private router: Router,
private http: HttpClient
) { }
ngOnInit() {
this.getRandomUser();
this.hubConnection = new HubConnectionBuilder().withUrl("http://localhost:5000/api/chat", {
skipNegotiation: true,
transport: HttpTransportType.WebSockets
}).build();
this.hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log('Error while establishing connection :('));
this.hubConnection.on('ReceiveMessage', (author: string, receivedMessage: IMessage) => {
this.messages.push(receivedMessage);
});
}
private getRandomUser() {
this.http.get("https://randomuser.me/api/")
.subscribe(
(data: any) => {
console.log(data);
this.iconLink = data.results[0].picture.medium;
this.author = data.results[0].name.first;
console.log(this.iconLink);
}
);
}
public sendMessage(): void {
var today = new Date();
this.messageData = {
author: this.author,
message: this.message,
timestamp: ("0" + today.getHours()).toString().slice(-2) + ":" + ("0" + today.getMinutes()).toString().slice(-2),
iconLink: this.iconLink
};
this.hubConnection
.invoke('SendToAll', this.author, this.messageData)
.catch(err => console.error(err));
this.message = '';
}
}
angular client view
<div class="outer-chat-container">
<div class="chat-container">
<ng-container *ngIf="messages.length > 0">
<div class="message-box" *ngFor="let message of messages">
<img class="avatar" [src]='message.iconLink'>
<div class="content">
<div class="headline">
<p class="author">{{ message.author }}</p>
<div class="datetime">{{ message.timestamp }}</div>
</div>
<p class="message">{{ message.message }}</p>
</div>
</div>
</ng-container>
</div>
<form class="send-message-form" (ngSubmit)="sendMessage()" #chatForm="ngForm">
<div>
<input type="text" name="message" [(ngModel)]="message" placeholder="Your message" required>
<button type="submit">Send</button>
</div>
</form>
</div>
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ProjectConker.Models;
using ProjectConker.Roadmaps;
using ProjectConker.Searching;
namespace ProjectConker
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddHttpClient();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("*")
.AllowCredentials();
}));
services.AddSignalR();
services.AddEntityFrameworkSqlServer();
// services.AddIdentity<IdentityUser, IdentityRole>();
services.AddDbContext<ConkerDbContext>(
options => options.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));
services.AddScoped<SearchEngine>();
services.AddTransient<RoadmapService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/api/chat");
});
}
}
}
C:\Users\wasii\Documents\CodingProjects\ProjectConker\ClientApp>ng version
Your global Angular CLI version (8.3.5) is greater than your local
version (6.0.8). The local Angular CLI version is used.
To disable this warning use "ng config -g cli.warnings.versionMismatch false".
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 6.0.8
Node: 10.16.3
OS: win32 x64
Angular: 6.1.10
... animations, common, compiler, compiler-cli, core, forms
... http, platform-browser, platform-browser-dynamic
... platform-server, router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.6.8
@angular-devkit/build-angular 0.6.8
@angular-devkit/build-optimizer 0.6.8
@angular-devkit/core 0.6.8
@angular-devkit/schematics 0.6.8
@angular/cli 6.0.8
@angular/language-service 6.0.5
@ngtools/webpack 6.0.8
@schematics/angular 0.6.8
@schematics/update 0.6.8
rxjs 6.2.1
typescript 2.7.2
webpack 4.8.3
C:\Users\wasii\Documents\CodingProjects\ProjectConker>dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 2.2.402
Commit: c7f2f96116
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\2.2.402\
Host (useful for support):
Version: 2.2.7
Commit: b1e29ae826
.NET Core SDKs installed:
2.2.402 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
Upvotes: 3
Views: 4752
Reputation: 25350
Let's review your ChatComponent
Component, it initializes and tries to set up a connection to the Hub. Its ngOnInit()
returns immediately after it binds an event handler for ReceiveMessage
.
ngOnInit() {
...
this.hubConnection = new HubConnectionBuilder().withUrl(...).build();
this.hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log('Error while establishing connection :('));
this.hubConnection.on('ReceiveMessage', ...);
}
Note it will take some time to connect to the Hub. If you try to send messages to the server before the connection is set up, it throws.
Create a thenable
to ensure the connection has been set up.
private thenable: Promise<void> ngOnInit() { ... this.hubConnection = new HubConnectionBuilder().withUrl(...).build(); this.start() this.hubConnection.on('ReceiveMessage', ...); } private start() { this.thenable = this.hubConnection.start(); this.thenable .then(() => console.log('Connection started!')) .catch(err => console.log('Error while establishing connection :(')); }
Whenever you want to communicate with the Hub, invoke by thenable
in promise-style. For example, your sendMessage()
should be changed in the following way:
public sendMessage(): void { this.thenable.then(() =>{ var today = new Date(); this.messageData = ... this.hubConnection .invoke('SendToAll', this.author, this.messageData) .catch(err => console.error(err)); this.message = ''; }); }
[Update]
There're two other bugs in your code :
You registered the SignalR middleware after the Spa middleware. Be aware UseSpa()
should be used as a catch-all middleware. You should invoke UseSignalR()
before UseSpa()
:
app.UseSignalR(routes => { routes.MapHub<ChatHub>("/api/chat"); }); app.UseMvc(routes => { ... }); app.UseSpa(spa => { ... });app.UseSignalR(routes =>{routes.MapHub<ChatHub>("/api/chat");});
If you're connecting to the same server, avoid using hard-coded url:
this.hubConnection = new HubConnectionBuilder().withUrl("http://localhost:5000/api/chat", { skipNegotiation: true, transport: HttpTransportType.WebSockets }).build();
Otherwise, it fails when you're using a different port/protocol.
Upvotes: 3