nkforce
nkforce

Reputation: 39

SignalR and React - Invocation canceled due to the underlying connection being closed

Good evening. I am building a chat component using SignalR(5.0.1, I also tried an early version 3.0.0) and dotnet core 3.1.

The comment that any of the users makes can be sent to all the connected clients, and all the connected clients can see the comment. However, the client which sent the comment will disconnect, and an error is caught addComment() in the mobx store.

A client will lose its connection after sending a comment (the sender and the other clients can get the comment which has been processed from ChatHub). I don't know why the server decides to drop the connection and raise an error. I

The error:

[2021-01-10T22:38:14.314Z] Information: Connection disconnected.
adventureStore.ts:73 Error: Invocation canceled due to the underlying connection being closed.
    at HubConnection.connectionClosed (HubConnection.ts:654)
    at HttpConnection.HubConnection.connection.onclose (HubConnection.ts:103)
    at HttpConnection.stopConnection (HttpConnection.ts:488)
    at WebSocketTransport.transport.onclose (HttpConnection.ts:410)
    at WebSocketTransport.close (WebSocketTransport.ts:135)
    at WebSocket.webSocket.onclose (WebSocketTransport.ts:97)

Here are the codes:

startup:

public class Startup
    {
            // only showing the codes related to problem
            services.AddSignalR();
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(opt =>
                {
                    opt.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = key,
                        ValidateAudience = false,
                        ValidateIssuer = false
                    };
                    opt.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = context => 
                        {
                            var accessToken = context.Request.Query["access_token"];
                            var path = context.HttpContext.Request.Path;
                            if (!string.IsNullOrEmpty(accessToken) && 
                                (path.StartsWithSegments("/chat")))
                            {
                                context.Token = accessToken;
                            }
                            return Task.CompletedTask;
                        }
                    };
                });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseMiddleware<ErrorHandlingMiddleware>();
            if (env.IsDevelopment())
            {
                // app.UseDeveloperExceptionPage();
            }

            // app.UseHttpsRedirection();

            app.UseRouting();
            app.UseCors("CorsPolicy");

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<ChatHub>("/chat");
            });
        }
    }

ChatHub.cs:

public class ChatHub : Hub
    {
        private readonly IMediator _mediator;
        public ChatHub(IMediator mediator)
        {
            _mediator = mediator;
        }

        public async Task SendComment(Create.Command command)
        {
            var username = Context.User?.Claims?.FirstOrDefault(x => x.Type == 
            ClaimTypes.NameIdentifier)?.Value;

            command.Username = username;

            var comment = await _mediator.Send(command);
            
            // clients can successfully receive the comment and load it to their mobx store
            await Clients.All.SendAsync("ReceiveComment", comment);
        }
    }

the mobx store where the connection is built:

  @observable comments: IComment[] | null = null;
  @observable.ref hubConnection: HubConnection | null = null;

  @action createHubConnection = () => {
    this.hubConnection = new HubConnectionBuilder()
      .withUrl("http://localhost:5000/chat", {
        accessTokenFactory: () => this.rootStore.commonStore.token!,
      })
      .configureLogging(LogLevel.Information)
      // i have to do this for now. Need to fix that disconnecting right
      // after Clients.All.SendAsync("ReceiveComment", comment); in ChatHub.cs
      // .withAutomaticReconnect([0, 0, 10000])
      .build();
    
    this.hubConnection
      .start()
      .then(() => console.log(this.hubConnection!.state))
      .catch((error) => console.log("Error establishing connection: ", error));

    this.hubConnection.on("ReceiveComment", (comment) => {
      runInAction(() => {
        if (this.comments == null) {
          this.comments = new Array<IComment>();
        }
        this.comments.push(comment);
      });
    });
  };

  @action stopHubConnection = () => {
    this.hubConnection!.stop();
  };
  
  @action addComment = async (values: any) => {
    try {
      await this.hubConnection!.invoke("SendComment", values);
    } catch (error) {
      console.log(error);
    }
  };

Upvotes: 0

Views: 5403

Answers (2)

Andrey Ravkov
Andrey Ravkov

Reputation: 1494

I've faced the same issue. I've use System.Text.Json as default json serializer. To fix this issue i upgrade 'Microsoft.AspNetCore.SignalR.Protocols.Json' nuget to the valid version.

Who is using nginx for reverse proxy please update your nginx.config: microsoft docs

These steps help me to resolve the same issue.

Upvotes: 1

ChinaHelloWorld
ChinaHelloWorld

Reputation: 1047

I have an Angular client facing same issues when connect to SignalR Hub in Asp.net Core 3.1. The applied NuGet package is "Microsoft.AspNetCore.SignalR.Core" version 1.1.0. After upgrade Asp.net Web API portal to .Net 5.0. The problem is resolved.

Upvotes: 1

Related Questions