我很难用signalR核心mvc 6来配置和使用.net。signalR集线器的目的是在调用C#控制器中的方法后,向js客户端发送消息(js客户机是以MVC作为ClientApp配置的应用程序)。
我为客户端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客户端无法通过start方法。相反,过了一段时间,它会抛出一条错误消息。
下面是我的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!!");
}
}
以及示例控制器,我想在其中发送消息给客户端:
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。
发布于 2022-02-08 08:21:21
您可以尝试在您的serverTimeout和KeepAliveInterval中设置program.cs:
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的值设置为最大值。
发布于 2022-09-29 17:23:50
这与.NET正在设置的SPA代理有关。
我得到的消息如下: 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到这个端口,这是没有代理的。
在我的例子中,dotnet服务的端口实际上是https://localhost:7088 (properties/ reality ings.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();
对我来说这解决了问题。
https://stackoverflow.com/questions/70976850
复制相似问题