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