[英]SignalR js client is not able to start connection, even if logs are showing that connection is being made (only LongPooling works)
我正在努力配置和使用 signalR 和 .net 核心 mvc 6。signalR 集线器的目的是在调用 C# controller 中的方法后向 js 客户端发送消息(js 客户端是在 MVC 中配置为 ClientApp 的 React 应用程序)。
我为客户端 signalR 实例和 asp.net 启用了调试
这是来自 ASP.NET 的日志:
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客户端日志:
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 客户端应用程序的示例代码:
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 客户端无法通过启动方法。 相反,一段时间后它会抛出一条错误消息。
下面是我的 Program.cs 文件,我在其中配置了 signalR(我怀疑这里可能有问题)
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>();
}
我的集线器的定义:
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!!");
}
}
并从我想向客户发送消息的地方采样 controller:
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() 方法中一段时间,并抛出错误消息“错误:服务器超时已过,未收到来自服务器的消息。”。
更新:当我在 js clint 中将传输类型显式设置为 LongPolling hub 时,它按预期工作,但这不是理想的解决方案。
--- 更新 --- 所有这些问题都只发生在本地机器上。 我试图检查我的运气并部署到生产应用程序,传输固定到 SSE,它没有任何问题,以及 WebSocket 传输。 我唯一的线索是,当我托管我的应用程序时,在本地主机应用程序上使用 kestrel,而在服务器上使用 IIS。
您可以尝试在 program.cs 中设置 serverTimeout 和 KeepAliveInterval:
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 的值设置为最大值。
这与.NET设置的SPA Proxy有关。
我收到的消息如下:Utils.ts:193
[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 到这个端口,这是 -not- 代理。
在我的例子中,do.net 服务端口实际上是 https://localhost:7088 (properties/launchsettings.json)
所以修改你的 Cors 设置...如下所示,如果 spaproxy 例如在 44423 和 .net 服务端口在 7088。
services.AddCors(options =>
{
options.AddPolicy("ClientPermission", policy =>
{
policy.AllowAnyHeader()
.AllowAnyMethod()
.WithOrigins("https://localhost:44423", "https://localhost:7088")
.AllowCredentials();
});
});
接下来是您的 js 代码,如下所示:
const newConnection = new HubConnectionBuilder()
.withUrl('https://localhost:7088/hub/myhub')
.withAutomaticReconnect()
.build();
对我来说,这解决了它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.