We recently built an ASP.NET Core API with Angular, and I'm attempting to implement a basic one-on-one chat functionality using SignalR. We're mapping User IDs to SignalR Connection IDs in a group, and then sending messages directly to users based on their User ID. 

It's working fine when tested with an MVC application, but when I try to use SignalR in my Angular project, it's giving me an exception: 'Failed to connect. Error during negotiation request.'

So in this post, we are going to discuss possible reasons for that error and provide solutions for the issue. let me tell that you that error Angular SignalR "Failed to complete negotiation with the server" error occurs when there is any negotiation issue between client and server, in our case the client is Angular application and the server is our backend api code.

If you are new to SignalR concept and have just started learning about SignalR then this post is very important for you,  In this article we have discussed from basic concept to advanced concept, in these we have discussed how to use that SignalR hub with a JavaScript client in detail.


1.SignalR Hub Configuration: 

First you need to make sure that the SignalR Hub in your ASP.NET Core API server is correctly configured and registered in your application, verify that it is set up to handle conversation requests, and establish a connection with the client and also, check if it is registered in the program.cs file, negotiation error is very common if SignalR is not configured correctly.

SignalR Hub Configuration:


[AllowAnonymous]
    public class ChatHub : Hub
    {
        public override Task OnDisconnectedAsync(Exception exception)
        {
            Debug.WriteLine("Client disconnected: " + Context.ConnectionId);
            if (OnlineUsers.onlineUsers.Any(a => a.ConnectionId == Context.ConnectionId))
            {
                OnlineUsers.onlineUsers.RemoveAll(a => a.ConnectionId == Context.ConnectionId);
            }
            return base.OnDisconnectedAsync(exception);
        }
        public override Task OnConnectedAsync()
        {
            Debug.WriteLine("Client connected: " + Context.ConnectionId);
            return base.OnConnectedAsync();
        }
        //create for each user to chat sepearte
        public void SetUserChatGroup(string userChatId)
        {
            var id = Context.ConnectionId;
            Debug.WriteLine($"Client {id} added to group " + userChatId);
            if (OnlineUsers.onlineUsers == null)
                OnlineUsers.onlineUsers = new List<OnlineUserModel>();

            if (!OnlineUsers.onlineUsers.Any(a => a.ConnectionId == Context.ConnectionId))
            {
                OnlineUsers.onlineUsers.Add(new OnlineUserModel { ConnectionId = Context.ConnectionId, ChatId = userChatId });
            }
            Groups.AddToGroupAsync(Context.ConnectionId, userChatId);
        }
       
        //send message to user
        public async Task SendMessageToGroup(string senderChatId, string receiverChatId, string message)
        {
            var chatHistory = await new DbChatHistoryLog().InsertChatHistory(new ChatModel { SenderId = senderChatId, ReciverId = receiverChatId, Message = message });
            if (chatHistory != null && chatHistory.ChatId > 0 && Clients!=null)
            {
                await Clients.Group(receiverChatId).SendAsync("ReceiveMessage", chatHistory.SenderId, chatHistory.SenderName, chatHistory.ReciverId, chatHistory.Message);
            }
        }
        
    }

2.CORS (Cross-Origin Resource Sharing) Configuration: 

Check that the CORS configuration on the server-side allows requests from the Angular application's domain. If your server side does not allow the client domain then the client will never be able to connect to the server, so it is important to check your application's CORS setting.

app.UseCors(builder => builder
    .AllowAnyHeader()
    .AllowAnyMethod()
    .SetIsOriginAllowed((host) => true)
    .AllowCredentials()
  );
Program.cs configuration:
using SignalRDemo.Chat;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddSignalR();
builder.Services.AddSingleton<ChatingHub>();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

//Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

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

app.UseAuthorization();

app.MapControllers();
app.MapHub<ChatingHub>("/ChatingHub");
app.Run();
Startup.cs configuration:
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.AddOpenApiDocument(c =>
            {
                c.Title = "SignalR API";
            });
            services.AddCors(o => o.AddPolicy("AllowOrigin", builder =>
            {
                builder.AllowAnyOrigin()
                       .AllowAnyMethod()
                       .AllowAnyHeader();
            }));
            
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddSignalR();          
            services.AddSingleton<ChatingHub>();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {


           

            app.UseStaticFiles(); // For the wwwroot folder
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            
            app.UseCors(builder => builder
                .AllowAnyHeader()
                .AllowAnyMethod()
                .SetIsOriginAllowed((host) => true)
                .AllowCredentials()
              );
            app.UseHttpsRedirection();
            // Make sure you call this before calling app.UseMvc()
            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatingHub>("/ChatingHub");
            });
            app.UseMvc();
            //Register the Swagger generator      
            app.UseOpenApi();
            app.UseSwaggerUi3();
        }
    }
    

3.Authentication and Authorization: 

If you are using using authentication or authorization in you applcation,then you also need to check that SignalR negotiation requests are authenticated and authorized because if the client request is not authenticated and authorized the server application will deny the connection request. SignalR endpoint is publicly available means it does not require any security checks, then decorate your hub with the [AllowAnonymous] attribute, this will allow clients to connect to your hub.

4.Firewall or Network Restrictions: 

Also check if there are any firewall or network restrictions in you server where you hosted the SignalR server that might be blocking the negotiation requests between the client and server.
You can use browser developer tools to inspect the network requests and responses during the 
negotiation process between clinet and server and check specific errors or issues occurring during the negotiation.

5.Check Version : 

You also need make sure that the versions of SignalR used in both the server-side (ASP.NET Core API) and the client-side (Angular) are compatible with each other, it means is that if your server side SignalR version is low and Client side SignalR version is low then your connection is worried,So try to have the same version in both the applications.

6.Make sure using correct Hub Url and try with skipNegotiation=true

skipNegotiation: true 
This option indicates whether to skip the negotiation process before establishing the connection. 
As we know thta negotiation is the process by which the client and server agree on a communication protocol. Setting skipNegotiation to true means that negotiation will be skipped, and the client assumes that the server supports the specified transport.

onnection = new signalR.HubConnectionBuilder()
    .configureLogging(signalR.LogLevel.Debug)
    .withUrl("http://localhost:4587/ChatHub", {
      skipNegotiation: true,
      transport: signalR.HttpTransportType.WebSockets
    })
    .build();
By following these solutions you should be able to resolve the "Failed to complete negotiation with the server" error in your Angular/SignalR application