首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SignalR js客户端无法启动连接,即使日志显示正在建立连接(只有LongPooling工作)。

SignalR js客户端无法启动连接,即使日志显示正在建立连接(只有LongPooling工作)。
EN

Stack Overflow用户
提问于 2022-02-03 19:00:13
回答 2查看 2.3K关注 0票数 0

我很难用signalR核心mvc 6来配置和使用.net。signalR集线器的目的是在调用C#控制器中的方法后,向js客户端发送消息(js客户机是以MVC作为ClientApp配置的应用程序)。

我为客户端signalR实例和asp.net启用了调试

下面是来自ASP.NET的日志:

代码语言:javascript
运行
复制
      SPA proxy is ready. Redirecting to https://localhost:44440.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager[1]
      New connection ctA6QHwS4fvVGcufYvtlAA created.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[10]
      Sending negotiation response.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[4]
      Establishing new connection.
dbug: Microsoft.AspNetCore.SignalR.HubConnectionHandler[5]
      OnConnectedAsync started.
dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubProtocolResolver[2]
      Found protocol implementation for requested protocol: json.
dbug: Microsoft.AspNetCore.SignalR.HubConnectionContext[1]
      Completed connection handshake. Using HubProtocol 'json'.
connected!! ctA6QHwS4fvVGcufYvtlAA

并对应于它们与js客户端的日志:

代码语言:javascript
运行
复制
Utils.ts:194 [2022-02-03T18:40:17.568Z] Debug: Starting HubConnection.
Utils.ts:194 [2022-02-03T18:40:17.568Z] Debug: Starting connection with transfer format 'Text'.
Utils.ts:194 [2022-02-03T18:40:17.576Z] Debug: Sending negotiation request: https://localhost:44440/hubs/order/negotiate?negotiateVersion=1.
Utils.ts:194 [2022-02-03T18:40:21.741Z] Debug: Skipping transport 'WebSockets' because it was disabled by the client.
Utils.ts:194 [2022-02-03T18:40:21.742Z] Debug: Selecting transport 'ServerSentEvents'.
Utils.ts:194 [2022-02-03T18:40:21.742Z] Trace: (SSE transport) Connecting.
Utils.ts:190 [2022-02-03T18:40:25.857Z] Information: SSE connected to https://localhost:44440/hubs/order?id=fxqgKpJnF5Dq5MX-RCfXcg
Utils.ts:194 [2022-02-03T18:40:25.857Z] Debug: The HttpConnection connected successfully.
Utils.ts:194 [2022-02-03T18:40:25.857Z] Debug: Sending handshake request.
Utils.ts:194 [2022-02-03T18:40:25.858Z] Trace: (SSE transport) sending data. String data of length 32.
Utils.ts:194 [2022-02-03T18:40:29.969Z] Trace: (SSE transport) request complete. Response status: 200.
Utils.ts:190 [2022-02-03T18:40:29.978Z] Information: Using HubProtocol 'json'.
Utils.ts:194 [2022-02-03T18:40:59.997Z] Debug: HttpConnection.stopConnection(undefined) called while in state Disconnecting.
index.js:1 [2022-02-03T18:40:59.997Z] Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message from the server.'.
console.<computed> @ index.js:1
Utils.ts:194 [2022-02-03T18:40:59.997Z] Debug: HubConnection.connectionClosed(Error: Server timeout elapsed without receiving a message from the server.) called while in state Connecting.
Utils.ts:194 [2022-02-03T18:40:59.997Z] Debug: Hub handshake failed with error 'Error: Server timeout elapsed without receiving a message from the server.' during start(). Stopping HubConnection.
Utils.ts:194 [2022-02-03T18:40:59.997Z] Debug: Call to HttpConnection.stop(Error: Server timeout elapsed without receiving a message from the server.) ignored because the connection is already in the disconnected state.
Utils.ts:194 [2022-02-03T18:40:59.997Z] Debug: HubConnection failed to start successfully because of error 'Error: Server timeout elapsed without receiving a message from the server.'.

以下是js客户端应用程序的示例代码:

代码语言:javascript
运行
复制
    console.log("hub attached");

    const hubConnection = new HubConnectionBuilder()
      .withUrl(OrderHubUrl, {
        transport: HttpTransportType.ServerSentEvents,
        accessTokenFactory: () => user.accessToken ?? "",
      })
      .withAutomaticReconnect()
      .configureLogging(LogLevel.Trace)
      .build();

    this.dispatcher.state.saveState("hubConnection", hubConnection);

    const startConnection = async () => {
      try {
        await hubConnection.start();
        console.log("connected");
      } catch (e) {
        this.dispatcher.dispatch(Event.ShowModal, {
          actionName: "OK",
          header: "Error",
          content: e,
        });
      }
    };
    hubConnection.on("ReceiveMessage", (user, message) => {
      console.log("message received");
      console.log(user);
      console.log(message);
    });

    hubConnection.onreconnecting((e) => console.log("reconnecting", e));
    hubConnection.onreconnected((e) => console.log("reconnected", e));
    startConnection();
  } 

从顶部的日志中可以看到,signalR js客户端无法通过start方法。相反,过了一段时间,它会抛出一条错误消息。

下面是我的Program.cs文件,其中配置了signalR (我怀疑这里可能有问题)

代码语言:javascript
运行
复制
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using OrderMaker.Authentication.Helpers;
using OrderMaker.Authentication.Services;
using OrderMaker.Entities;
using OrderMaker.Modules.Common.Services;
using OrderMaker.Modules.Order.Hubs;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllersWithViews();
builder.Services.AddSignalR(c =>
{
    c.EnableDetailedErrors = true;
    c.ClientTimeoutInterval = TimeSpan.MaxValue;
    c.KeepAliveInterval = TimeSpan.MaxValue;
});

ConfigureConfiguration(builder.Configuration);
ConfigureServices(builder.Services, builder.Configuration);

var app = builder.Build();

app.UseAuthentication();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    // 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.UseCors(
          x => x
          .AllowAnyMethod()
          .AllowAnyHeader()
          .AllowCredentials());

app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");

app.MapHub<OrderHub>("/hubs/order");

app.Run();


void ConfigureConfiguration(ConfigurationManager configuration)
{
    using var client = new OrderMakerContext();
    client.Database.EnsureCreated();
}

void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
    services.AddControllers();
    // configure strongly typed settings objects
    var appSettingsSection = configuration.GetSection("AppSettings");
    services.Configure<AppSettings>(appSettingsSection);
    // configure jwt authentication
    var appSettings = appSettingsSection.Get<AppSettings>();
    var key = Encoding.ASCII.GetBytes(appSettings.Secret);
    services.AddAuthentication(x =>
    {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(x =>
    {
        x.RequireHttpsMetadata = false;
        x.SaveToken = true;
        x.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false
        };
        x.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                context.Token = context.Request.Cookies["order_maker_token"];
                // If the request is for our hub...
                var path = context.HttpContext.Request.Path;
                var accessToken = context.Request.Query["access_token"];
                if (!string.IsNullOrEmpty(accessToken) &&
                    (path.StartsWithSegments("/hubs/")))
                {
                    // Read the token out of the query string
                    context.Token = accessToken;
                }
                return Task.CompletedTask;
            }
        };
    }
    );

    services.AddScoped<IUserService, UserService>();
    services.AddSingleton<ILogService, LogService>();
}

我的枢纽的定义:

代码语言:javascript
运行
复制
public class OrderHub : Hub
    {
        public async Task SendNotification(List<OrderModel> message)
        {
            await Clients.All.SendAsync("ReceiveMessage", message);
        }
        public override async Task OnConnectedAsync()
        {
            await base.OnConnectedAsync();
            Console.WriteLine("connected!! " + Context.ConnectionId);
        }
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            await base.OnDisconnectedAsync(exception);
            Console.WriteLine("disconnected!!");
        }
    }

以及示例控制器,我想在其中发送消息给客户端:

代码语言:javascript
运行
复制
  private readonly IHubContext<OrderHub> _orderHubContext;
        public OrdersController(IHubContext<OrderHub> orderHubContext)
        {
            _orderHubContext = orderHubContext;
        }

       
        [Route("api/[controller]/")]
        [HttpPost]
        //[Authorize(Roles = $"{Role.Admin},{Role.Manager},{Role.Employee}")]
        public async Task<IActionResult> Post([FromBody] List<OrderModel> model)
        {
               /// some code for creating entities etc
               db.SaveChanges();
               /// notification to all clients that something new was added
               Console.Write(_orderHubContext.Clients.All.ToString());
               await _orderHubContext.Clients.All.SendAsync("ReceiveMessage", "hi there");
                }           
            return Ok(new MessageResponse("Order added succesfully."));
    }

基本上我是迷路了,我已经花了两天的时间找出问题的原因,但我就是不能让这件事起作用。我真的很感激任何建议或帮助。我试图禁用防火墙,使用不同的浏览器等,但没有成功。连接到集线器,c#应用程序会看到这个新连接,但js客户端只是在start()方法中停留了一段时间,并抛出了一个错误消息' error :服务器超时已过而没有从服务器接收消息‘。

更新:当我显式地设置js到LongPolling集线器的传输类型时,它是按预期工作的,但这不是理想的解决方案。

-更新--所有这些问题都只发生在本地机器上。我试着检查自己的运气,并将其部署到生产应用程序中,并将传输固定到SSE,并且没有任何问题,也可以使用WebSocket传输。我唯一的线索是,在本地主机上,应用程序使用的是kestrel,而在服务器上,当我承载我的应用程序时,则使用IIS。

EN

回答 2

Stack Overflow用户

发布于 2022-02-08 08:21:21

您可以尝试在您的serverTimeout和KeepAliveInterval中设置program.cs:

代码语言:javascript
运行
复制
builder.Services.AddSignalR(c =>
{
    c.EnableDetailedErrors = true;
    c.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
    c.KeepAliveInterval = TimeSpan.FromSeconds(15);
});

原因:

如果服务器没有在此间隔内发送消息,则会自动发送ping消息以保持连接处于打开状态。更改KeepAliveInterval时,请更改客户端上的ServerTimeout或serverTimeoutInMilliseconds设置。推荐的ServerTimeout或serverTimeoutInMilliseconds值是KeepAliveInterval值的两倍。。因此,最好不要将KeepAliveInterval的值设置为最大值。

票数 0
EN

Stack Overflow用户

发布于 2022-09-29 17:23:50

这与.NET正在设置的SPA代理有关。

我得到的消息如下: Utils.ts:193

代码语言:javascript
运行
复制
      [2022-09-29T17:09:19.866Z] Error: Failed to complete negotiation with the server: Error: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /hub/negotiate</pre>
</body>
</html>
: Status code '404' Either this is not a SignalR endpoint or there is a proxy blocking the connection.

可能发生的情况是,代理将流量从spa路由到csproj文件中这里指定的端口。https://localhost:44423,您不应该启动一个websocket到这个端口,这是没有代理的。

在我的例子中,dotnet服务的端口实际上是https://localhost:7088 (properties/ reality ings.json)

所以修改你的Cors设置..。如下图所示,如果spaproxy为44423,.net服务端口为7088。

代码语言:javascript
运行
复制
 services.AddCors(options =>
        {
            options.AddPolicy("ClientPermission", policy =>
            {
                policy.AllowAnyHeader()
                    .AllowAnyMethod()
                    .WithOrigins("https://localhost:44423", "https://localhost:7088")
                    .AllowCredentials();
            });
        });

接下来是js代码,如下所示:

代码语言:javascript
运行
复制
 const newConnection = new HubConnectionBuilder()
        .withUrl('https://localhost:7088/hub/myhub')
        .withAutomaticReconnect()
        .build();

对我来说这解决了问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70976850

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档