John
John

Reputation: 521

Angular/SignalR Error: Failed to complete negotiation with the server

Using SignalR for my server and Angular for my client... When I run my client I receive these errors:

zone.js:2969 OPTIONS https://localhost:27967/chat/negotiate 0 ()

Utils.js:148 Error: Failed to complete negotiation with the server: Error

Utils.js:148 Error: Failed to start the connection: Error

I am guessing it is something with CORS... I am trying to implement a simple chat application. I am using the latest verison of SignalR:

Here is the github that contains the code for the tutorial I am following. SignalR Chat Tutorial

Here is my startup

    using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace signalrChat
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
            {
                builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .WithOrigins("http://localhost:4200");
            }));

            services.AddSignalR();
        }

        // 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();
            }

            app.UseCors("CorsPolicy");

            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/chat");
            });
        }
    }
}

And here is my client:

    import { Component, OnInit } from '@angular/core';
import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  private hubConnection: HubConnection;

  nick = '';
  message = '';
  messages: string[] = [];

  ngOnInit() {
    this.nick = window.prompt('Your name:', 'John');

    this.hubConnection = new HubConnectionBuilder().withUrl('https://localhost:27967/chat').build();

    this.hubConnection
    .start()
    .then(() => console.log("Connection Started!"))
    .catch(err => console.log("Error while establishing a connection :( "));

    this.hubConnection.on('sendToAll', (nick: string, receiveMessage: string) => {
      const text = `${nick}: ${receiveMessage}`;
      this.messages.push(text);
    })
  }

  public sendMessage(): void {
    this.hubConnection
    .invoke('sendToAll', this.nick, this.message)
    .catch(err => console.log(err));
  }

}

I assume it may be something with cors. Thank you!

EDIT: I just recreated the signalr implementation in visual studio and it worked. I believe I chose the wrong settings on start up.

Upvotes: 45

Views: 103511

Answers (8)

Caims
Caims

Reputation: 739

connection = new signalR.HubConnectionBuilder()
    .configureLogging(signalR.LogLevel.Debug)  // add this for diagnostic clues
    .withUrl("http://localhost:5000/decisionHub", {
      skipNegotiation: true,  // skipNegotiation as we specify WebSockets
      transport: signalR.HttpTransportType.WebSockets  // force WebSocket transport
    })
    .build();

Upvotes: 73

Daniel
Daniel

Reputation: 333

I had the same error due to cross origin. This solved it for me program.cs (dotnet 6) or startup.cs (dotnetcore < 6)

app.UseCors(builder => builder
            .AllowAnyHeader()
            .AllowAnyMethod()
            .SetIsOriginAllowed(_ => true)
            .AllowCredentials()
        );

Notice that you should not open all origins if it is not a development environment or special cases.

Upvotes: 0

Eternal21
Eternal21

Reputation: 4664

I was pointing to the wrong endpoint. I was using

https://localhost:5001/api/message-hub instead of

https://localhost:5001/message-hub (extra /api)

Also if you're using Angular, you're likely to get a Websocket not OPEN error right after fixing this one, so here's a link to save you from more searches.

Upvotes: 7

David Castro
David Castro

Reputation: 1957

In my case it wasn't necessary all those stuff, I was missing the https instead http, and it worked like a charm.

const connection = new signalR.HubConnectionBuilder()
  .configureLogging(signalR.LogLevel.Debug)
  .withUrl('https://localhost:44308/message')
  .build();

Upvotes: 3

German Gracia
German Gracia

Reputation: 360

I had the same problem, and it turns out that the launchSettings.json in the part where it says signalRchatServer does nothing, the url that worked with me was that of the iisexpress, I say it because there are many places where they say that the url is the one below .

enter image description here

Upvotes: 7

DSA
DSA

Reputation: 780

I waste almost two days for this and finally figured out,

When this error occurs?

  • When you upgrade your existing SignalR server project to .Net Core but do not upgrade the client
  • When you create the SignalR server using .Net core but you use traditional .Net framework for the client

Why this error occurs?

  • The error occurs because new SignalR does not allow you to use old server & new client or new server & old client

  • It means if you create the SignalR server using .Net core then you must create the client using .Net Core

This was the issue in my case.

Upvotes: 2

Rukshan Dangalla
Rukshan Dangalla

Reputation: 2590

I faced the slimier issue and I fixed it by adding

skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets

in client-side as @Caims mentioned. But I don't think this is the correct solution and feel more like a hack 😊. What you have to do is adding AllowCredentials in server side. Anyway when it's coming to Azure you can't relay on that fix. So no need enable WSS only in client side.

Here is my ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(o => o.AddPolicy("CorsPolicy", builder => {
        builder
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials()
        .WithOrigins("http://localhost:4200");
    }));

    services.AddSignalR();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

This is my Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors("CorsPolicy");
    app.UseSignalR(routes =>
    {
        routes.MapHub<NotifyHub>("/notify");
    });

    app.UseMvc();
}

And finally this is how I connected from client-side:

const connection = new signalR.HubConnectionBuilder()
      .configureLogging(signalR.LogLevel.Debug)
      .withUrl("http://localhost:5000/notify", {
        //skipNegotiation: true,
        //transport: signalR.HttpTransportType.WebSockets
      }).build();

connection.start().then(function () {
    console.log('Connected!');
}).catch(function (err) {
    return console.error(err.toString());
});

connection.on("BroadcastMessage", (type: string, payload: string) => {
    this.msgs.push({ severity: type, summary: payload });
});

Upvotes: 28

Sibeesh Venu
Sibeesh Venu

Reputation: 21719

I was facing the same problem in my Angular application when I try to connect to Azure SignalR service Azure Function.

[FunctionName("Negotiate")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, [SignalRConnectionInfo(HubName = "broadcast")] SignalRConnectionInfo info,
    ILogger log) {
    log.LogInformation("Negotiate trigger function processed a request.");
    return info != null ? (ActionResult) new OkObjectResult(info) : new NotFoundObjectResult("SignalR could not load");
}

And below was my init() function code in Angular service.

init() {
    this.getSignalRConnection().subscribe((con: any) => {
        const options = {
            accessTokenFactory: () => con.accessKey
        };

        this.hubConnection = new SignalR.HubConnectionBuilder()
            .withUrl(con.url, options)
            .configureLogging(SignalR.LogLevel.Information)
            .build();

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

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

My problem was with the con.accessKey. I just checked the properties of the SignalRConnectionInfo class and understood that I need to use accessToken instead of accessKey.

public class SignalRConnectionInfo {
    public SignalRConnectionInfo();

    [JsonProperty("url")]
    public string Url {
        get;
        set;
    }
    [JsonProperty("accessToken")]
    public string AccessToken {
        get;
        set;
    }
}

So after changing the code to accessTokenFactory: () => con.accessToken everything worked as usual.

Upvotes: 3

Related Questions