You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Windows Server 2022上IIS部署Blazor Server的网络连接问题

Blazor Server部署与运行异常问题排查

问题描述

  • 问题1(影响生产环境):在IIS、IIS Express和Kestrel环境中均出现错误:Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message from the server.',但Blazor Server模板应用无此超时问题。
  • 问题2:使用Kestrel开发时,浏览器访问HTTPS端口(https://localhost:5101/my-tags)出现连接意外关闭错误,HTTP端口可正常访问;Blazor Server模板应用在Kestrel的HTTPS端口也存在此问题。

相关配置信息

启动设置(launch settings)

"profiles": {
  "JBFTags": {
    "commandName": "Project",
    "launchBrowser": true,
    "launchUrl": "my-tags?login=jMVt+ABJXf87QX52eeGLOGAnA6BPYUPMnoqaZUiVvoc=",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    },
    "dotnetRunMessages": true,
    "applicationUrl": "https://localhost:5101;http://localhost:5100"
  },
  "JBFTagshttponly": {
    "commandName": "Project",
    "launchBrowser": true,
    "launchUrl": "my-tags?login=jMVt+ABJXf87QX52eeGLOGAnA6BPYUPMnoqaZUiVvoc=",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    },
    "dotnetRunMessages": true,
    "applicationUrl": "http://localhost:5100"
  },
  "IIS Express": {
    "commandName": "IISExpress",
    "launchBrowser": true,
    "launchUrl": "my-tags?login=m57T6dYR2EjA==",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    }
  }
},
"iisSettings": {
  "windowsAuthentication": false,
  "anonymousAuthentication": true,
  "iisExpress": {
    "applicationUrl": "http://localhost:19716",
    "sslPort": 44396
  }
}

Program.cs代码

using NLog;

var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("JBF Web App started!");
var builder = WebApplication.CreateBuilder(args);

try
{
    // file gets locked so close it in case it's open
    try
    {
        var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Telerik Reporting", "WebReportDesignerSettings.json");
        File.Create(path).Close();
    }
    catch (Exception ex)
    {
        logger.Warn("Path error:  {0}", ex.Message);
    }

    // Add services to the container.
    builder.Services.AddCors(c =>
    {
        c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
    });

    builder.Services.AddScoped<SizeService>();
    builder.Services.AddServerSideBlazor()
        .AddHubOptions(options =>
        {
            options.ClientTimeoutInterval = TimeSpan.FromMinutes(2); // Client timeout
            options.HandshakeTimeout = TimeSpan.FromMinutes(2); // Handshake timeout
            options.KeepAliveInterval = TimeSpan.FromMinutes(1); // Keep-alive interval
        });
    builder.Services.AddScoped<DialogService>();
    builder.Services.AddScoped<NotificationService>();
    builder.Services.AddScoped<TooltipService>();
    builder.Services.AddScoped<ContextMenuService>();
    builder.Services.AddScoped<IMyIdService, MyIdService>();
    builder.Services.AddScoped<SelectedTagsService>();
    builder.Services.AddScoped<JSServices>();
    builder.Services.AddScoped<ICookieService, CookieService>();
    builder.Services.AddHttpContextAccessor();
    builder.Services.AddSingleton<UserAgentDetecter>();
    builder.Services.AddSingleton<HostService>();
    builder.Services.AddSingleton<ExportService>();
    builder.Services.AddSingleton<TagsSqlService>();
    builder.Services.AddScoped<IIdentitySQLService, IdentitySQLService>();
    builder.Services.AddScoped<ToasterService>();
    builder.Services.AddScoped<ITagsEFRepo, TagsEFRepo>();
    builder.Services.AddScoped<IAspNetEfRepo, AspNetEfRepo>();

    builder.Services.AddControllers();
    builder.Services.AddRazorPages().AddNewtonsoftJson();

    builder.Services.AddDbContext<TagDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("JBFSqlServer2005")));

    builder.Services.AddDbContext<AspNetDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("JBFASPNETDB")));

    var reportsPath = Path.Combine(builder.Environment.ContentRootPath, "Reports");

    // Configure dependencies for ReportsController.
    builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp =>
        new ReportServiceConfiguration
        {
            ReportingEngineConfiguration = sp.GetService<IConfiguration>(),
            HostAppId = "JBFTags",
            Storage = new FileStorage(),
            ReportSourceResolver = new TypeReportSourceResolver()
                .AddFallbackResolver(new UriReportSourceResolver(reportsPath))
        });

    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowBlazorOrigin", builder =>
        {
            builder.WithOrigins("http://localhost:5000", "https://localhost:5001")
                   .AllowAnyHeader()
                   .AllowAnyMethod();
        });
    });

    // Configure dependencies for ReportDesignerController.
    builder.Services.TryAddSingleton<IReportDesignerServiceConfiguration>(sp => new ReportDesignerServiceConfiguration
    {
        DefinitionStorage = new FileDefinitionStorage(reportsPath, new[] { "Resources", "Shared Data Sources" }),
        ResourceStorage = new ResourceStorage(Path.Combine(reportsPath, "Resources")),
        SharedDataSourceStorage = new FileSharedDataSourceStorage(Path.Combine(reportsPath, "Shared Data Sources")),
        SettingsStorage = new FileSettingsStorage(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Telerik Reporting"))
    });

    // NLog: Setup NLog for Dependency injection
    builder.Logging.ClearProviders();
    builder.Host.UseNLog();
    var app = builder.Build();

    // Configure the HTTP request pipeline.
    app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
    app.UseCors("AllowBlazorOrigin");

    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();
    app.MapControllers();
    app.MapBlazorHub();
    app.MapFallbackToPage("/_Host");
    app.UseWebSockets();

    app.Run();

    // Log the ports the application is listening on
    logger.Info("listing ports");
    var serverAddresses = app.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>();
    foreach (var address in serverAddresses.Addresses)
    {
        logger.Info("Listening on: {0}", address);
    }
}
catch (Exception e)
{
    logger.Error(e, "Stopped program because of exception");
    throw;
}
finally
{
    NLog.LogManager.Shutdown();
}

Netstat输出及系统版本

TCP    127.0.0.1:5100         0.0.0.0:0              LISTENING  *** HTTP
TCP    127.0.0.1:5101         0.0.0.0:0              LISTENING  *** HTTPS
OS Name:                   Microsoft Windows Server 2022 Standard
OS Version:                10.0.20348 N/A Build 20348
OS Manufacturer:           Microsoft Corporation
OS Configuration:          Standalone Server
OS Build Type:             Multiprocessor Free
BIOS Version:              Proxmox distribution of EDK II 4.2023.08-4, 2/15/2024

排查建议

针对问题1(连接超时)

  1. 匹配客户端与服务端Hub配置:确认Blazor客户端的超时参数是否与服务端ClientTimeoutIntervalKeepAliveInterval一致,避免客户端提前判定连接超时。
  2. 排查主线程阻塞:检查Telerik Reporting文件操作、数据库查询等逻辑是否存在长时间阻塞,导致无法及时响应Hub心跳消息。
  3. 对比模板应用差异:逐一比对现有应用与Blazor Server模板的中间件顺序、服务注册项,排查自定义组件对Hub通信的干扰。
  4. 细化Hub日志:在Hub的OnConnectedAsyncOnDisconnectedAsync事件中添加详细日志,追踪超时发生的具体阶段。

针对问题2(HTTPS端口连接关闭)

  1. 验证HTTPS证书:重新生成并信任开发环境证书(执行dotnet dev-certs https --trust),检查证书是否有效。
  2. 调整中间件顺序:将app.UseWebSockets();移至app.UseRouting();之前,确保WebSocket中间件在路由匹配前生效。
  3. 更换HTTPS端口测试:临时修改Kestrel的HTTPS端口,排除端口冲突或环境限制问题。
  4. 禁用HTTPS重定向验证:临时注释app.UseHttpsRedirection();,确认是否因重定向逻辑导致连接异常。

内容的提问来源于stack exchange,提问作者Darryl Wagoner WA1GON

火山引擎 最新活动