Visual Studio 2026 多启动项目中 Web.Api 无法访问
结论
SocketException: actively refused 说明 Web.UI 发起请求时,localhost:7124 上没有进程监听。异常发生在 TCP 连接阶段,与 HTTPS 开发证书、IHttpClientFactory 或 JwtDelegatingHandler 本身无关。
Visual Studio 的多项目启动会同时发起各项目的启动操作,但无法保证以下几件事:
- 各项目使用了预期的
launchSettings.jsonprofile; - Web.Api 绑定端口后,Web.UI 才开始启动;
- Web.Api 通过健康检查后,Web.UI 才处理第一个请求。
需要分别解决启动配置和服务就绪两个问题:
- 显式指定 Web.Api 使用
httpsprofile,确认程序进入了Program.Main并成功绑定端口。 - Web.UI 调用 Web.Api 时增加就绪等待或重试,避免并发启动引发竞态。
只调整项目的排列顺序,无法保证 API 已经可用。现有信息也不足以判断 Web.Api 控制台持续为空是 Visual Studio 18.6.2 的已知缺陷。更可能的原因是多项目启动配置、调试目标,或 .vs 中保存的用户级启动状态有误。
确认 Visual Studio 实际启动的项目
在解决方案属性的 Startup Project 页面中选择 Multiple startup projects,检查以下设置:
- Web.Api 的 Action 为
Start; - Web.UI 的 Action 为
Start; - Web.Api 的调试目标为
https,没有选成 IIS Express、WSL、Docker 或其他 profile; - 当前 Solution Configuration 中,两个项目都勾选了
Build; - Web.Api 没有停在入口断点、异常断点或模块加载阶段。
按下 F5 后,打开 Visual Studio 的 Debug > Windows > Processes,检查是否存在 Web.Api 对应的进程。不要只凭 console window 判断项目是否已经运行。调试启动器可能已经创建窗口,但目标程序还没有进入应用入口,也可能在绑定端口前就退出了。
还要查看 Output 窗口中的以下输出源:
DebugBuildASP.NET Core Web Server
如果 Web.Api 进程立即退出,退出代码和启动错误通常会出现在这些输出中,不一定会写入项目的 console。
也可以在 Program.cs 最前面临时加入一条不依赖 Serilog 的输出:
Console.Error.WriteLine(
$"Web.Api entering Program.cs, PID={Environment.ProcessId}");
如果这条输出没有出现,说明问题发生在应用代码执行之前,应继续检查 Visual Studio 的启动目标和调试配置。如果输出出现了,却没有 Now listening on,再检查 builder.Build()、Serilog 初始化、依赖注入,以及启动时执行的数据库或网络操作。
显式使用 https launch profile
Web.Api 的 launchSettings.json 可以保留以下配置:
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7124;http://localhost:5247",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
端口绑定不依赖 dotnetRunMessages,但启用它后,可以更直观地确认 Kestrel 的监听地址。
Web.UI 也应配置一个名称明确的 profile,例如:
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7063;http://localhost:5063",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
修改后,在 Visual Studio 工具栏中分别选中两个项目,确认它们都能单独使用名为 https 的目标启动。
使用 .slnLaunch 保存多项目启动配置
较新的 Visual Studio 可以把多项目启动设置保存到解决方案级的 .slnLaunch 文件中。建议先在 Visual Studio 的多项目启动配置界面中创建并保存配置,再检查生成的内容,以免手写了当前版本不支持的字段。
典型配置如下:
[
{
"Name": "Web.Api + Web.UI",
"Projects": [
{
"Path": "Web.Api\\Web.Api.csproj",
"Action": "Start",
"DebugTarget": "https"
},
{
"Path": "Web.UI\\Web.UI.csproj",
"Action": "Start",
"DebugTarget": "https"
}
]
}
]
项目路径需要根据实际的解决方案目录调整。DebugTarget 用于选择 launchSettings.json 中的 profile。如果 Visual Studio 18.6.2 通过界面生成的文件没有这个字段,或者采用了不同的字段结构,应以该版本实际生成的配置为准,不要直接照搬示例。
.slnLaunch 负责指定启动哪些项目以及使用哪个调试目标。它通常不会等到 Web.Api 的 /health 返回成功后才启动 Web.UI,因此还要处理服务就绪竞态。
等待 Web.Api 就绪后再发起请求
即使 Web.Api 已被正确启动,两个项目仍会并发初始化。Web.UI 可能已经启动并开始访问 API,而 Web.Api 还在加载配置、执行迁移或初始化 Serilog。
可以为健康检查配置单独的客户端:
builder.Services.AddHttpClient("WebApiHealth", client =>
{
client.BaseAddress = new Uri(
builder.Configuration["WebApi:BaseUrl"]
?? throw new InvalidOperationException(
"Configuration 'WebApi:BaseUrl' is missing."));
client.Timeout = TimeSpan.FromSeconds(2);
});
在 Web.UI 开始处理请求前等待 API:
var app = builder.Build();
var clientFactory =
app.Services.GetRequiredService<IHttpClientFactory>();
var healthClient = clientFactory.CreateClient("WebApiHealth");
const int maxAttempts = 10;
var apiReady = false;
for (var attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
using var response =
await healthClient.GetAsync("/health");
if (response.IsSuccessStatusCode)
{
apiReady = true;
break;
}
}
catch (HttpRequestException) when (attempt < maxAttempts)
{
// API 进程可能尚未绑定端口。
}
catch (TaskCanceledException) when (attempt < maxAttempts)
{
// 单次健康检查超时,继续尝试。
}
await Task.Delay(TimeSpan.FromSeconds(1));
}
if (!apiReady)
{
throw new InvalidOperationException(
$"Web.Api did not become ready after {maxAttempts} attempts.");
}
await app.RunAsync();
如果 Web.UI 可以在 API 暂时不可用时继续运行,就不必抛出异常,可以改为记录错误并返回 503 Service Unavailable。选择哪种策略,取决于 Web.UI 是否允许降级运行。
业务请求也应设置有限次数的重试。不要无限重试,也不要重试明显无法自行恢复的 4xx 响应。
重建 Visual Studio 的用户级启动状态
如果 Web.Api 可以在 Visual Studio 中单独正常启动,但使用旧的多项目配置时总是出现空 console,可以重建 Visual Studio 保存的解决方案状态:
- 关闭 Visual Studio。
- 将解决方案目录下的
.vs文件夹重命名,例如.vs.backup。 - 重新打开解决方案。
- 重新创建多项目启动配置,并为两个项目明确选择
httpsprofile。 - 执行
Rebuild Solution,然后再次启动。
重命名后仍可在需要时恢复,比直接删除更稳妥。.vs 保存的是本机用户级状态,通常不包含项目源代码,但可能包含启动目标、调试设置和解决方案缓存。
如果问题仍能稳定复现,可以继续尝试:
- 将 Visual Studio 更新到当前可用的修复版本;
- 暂时禁用可能影响调试启动的扩展;
- 使用
Start Without Debugging对 Web.Api 进行对照测试; - 收集 Visual Studio
ActivityLog.xml、Output 窗口内容、目标进程退出代码和最小复现解决方案,然后提交反馈。
验证问题是否解决
启动多项目配置后,运行:
netstat -ano | findstr :7124
应当能看到监听记录。再运行:
curl.exe -k https://localhost:7124/health
应当得到成功响应。
-k 只适合本地诊断,用于排除客户端证书信任问题,不应写入正式应用代码。Web.UI 仍应通过受信任的开发证书正常访问 HTTPS。
如果 netstat 没有监听记录,应继续排查 Web.Api 的启动过程。如果端口已经处于监听状态,但 Web.UI 仍然请求失败,再检查 WebApi:BaseUrl 的环境覆盖、代理设置、IPv4/IPv6 解析以及 JwtDelegatingHandler。这两类故障需要分开排查。
备注:内容仅供参考。
