.NET 8 React SPA开发启动超时异常排查求助
问题概述
开发环境启动.NET 8 React SPA应用时,触发TimeoutException,错误提示create-react-app服务器未在超时时间内监听请求。已将启动超时延长至300秒仍报错,.NET 6环境下相同配置可正常运行;前端可通过URL直接访问页面,但ReactDevelopmentServerMiddleware仍触发超时。
环境配置
- .NET 8.0.204
- React TypeScript 18.2.0
- Node 18.20.0
- Visual Studio 17.9.6
- 使用
Microsoft.AspNetCore.SpaServices.Extensions配置SPA
相关配置代码
builder.Services.AddSpaStaticFiles(configuration => { configuration.RootPath = "WebClient/dist"; }); ..... app.UseSpa(spa => { spa.Options.SourcePath = "WebClient"; // Only in local development use npm start. Not in test/production. if (environment.IsDevelopment()) { spa.Options.StartupTimeout = TimeSpan.FromSeconds(300); spa.UseReactDevelopmentServer(npmScript: "dev"); } });
完整错误日志
info: Microsoft.AspNetCore.SpaServices[0] Starting create-react-app server on port 51062... info: Microsoft.AspNetCore.SpaServices[0] > paymentwebclient@0.0.0 dev info: Microsoft.AspNetCore.SpaServices[0] > vite info: Microsoft.AspNetCore.SpaServices[0] Port 5173 is in use, trying another one... info: Microsoft.AspNetCore.SpaServices[0] VITE v5.1.6 ready in 1359 ms info: Microsoft.AspNetCore.SpaServices[0] Γ₧£ Local: http://localhost:5174/ info: Microsoft.AspNetCore.SpaServices[0] Γ₧£ Network: use --host to expose dbug: Microsoft.Extensions.Hosting.Internal.Host[1] Hosting starting warn: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[16] The WebRootPath was not found: ****\wwwroot. Static files may be unavailable. info: Microsoft.Hosting.Lifetime[14] Now listening on: https://localhost:7252 info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5293 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development info: Microsoft.Hosting.Lifetime[0] Content root path: *** dbug: Microsoft.Extensions.Hosting.Internal.Host[2] Hosting started fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.TimeoutException: The create-react-app server did not start listening for requests within the timeout period of 120 seconds. Check the log output for error information. at Microsoft.AspNetCore.SpaServices.Extensions.Util.TaskTimeoutExtensions.WithTimeout[T](Task`1 task, TimeSpan timeoutDelay, String message) at Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer.ReactDevelopmentServerMiddleware.<>c__DisplayClass2_0.<<Attach>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.SpaServices.Extensions.Proxy.SpaProxy.PerformProxyRequest(HttpContext context, HttpClient httpClient, Task`1 baseUriTask, CancellationToken applicationStoppingToken, Boolean proxy404s) at Microsoft.AspNetCore.Builder.SpaProxyingExtensions.<>c__DisplayClass2_0.<<UseProxyToSpaDevelopmentServer>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context) fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.TimeoutException: The create-react-app server did not start listening for requests within the timeout period of 120 seconds. Check the log output for error information. at Microsoft.AspNetCore.SpaServices.Extensions.Util.TaskTimeoutExtensions.WithTimeout[T](Task`1 task, TimeSpan timeoutDelay, String message) at Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer.ReactDevelopmentServerMiddleware.<>c__DisplayClass2_0.<<Attach>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.SpaServices.Extensions.Proxy.SpaProxy.PerformProxyRequest(HttpContext context, HttpClient httpClient, Task`1 baseUriTask, CancellationToken applicationStoppingToken, Boolean proxy404s) at Microsoft.AspNetCore.Builder.SpaProxyingExtensions.<>c__DisplayClass2_0.<<UseProxyToSpaDevelopmentServer>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
问题原因
核心原因是.NET 8中ReactDevelopmentServerMiddleware的服务启动检测逻辑是针对传统create-react-app(基于Webpack)的日志输出设计的,而当前项目使用的是Vite作为构建工具,Vite的启动日志格式与Webpack不同,导致中间件无法识别服务已启动,最终触发超时。此外,日志中显示超时时间为120秒,说明手动设置的StartupTimeout未被中间件正确读取。
解决方案
方法一:固定Vite端口并手动配置代理
绕过ReactDevelopmentServerMiddleware的自动检测逻辑,直接将.NET请求代理到Vite服务端口:
- 修改Vite配置固定端口
在前端项目的vite.config.ts中指定固定端口,避免动态端口带来的代理问题:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { port: 5174 // 固定Vite服务端口 } })
- 修改.NET的SPA配置
替换UseReactDevelopmentServer为UseProxyToSpaDevelopmentServer,直接指定代理地址:
app.UseSpa(spa => { spa.Options.SourcePath = "WebClient"; if (environment.IsDevelopment()) { // 直接代理到固定的Vite服务地址 spa.UseProxyToSpaDevelopmentServer("http://localhost:5174"); } });
- 启动方式调整
可以选择两种启动方式:
- 先手动启动前端Vite服务(执行
npm run dev),再启动.NET项目; - 保持.NET自动启动Vite,此时.NET会调用
npm run dev启动服务,然后直接代理到固定端口。
方法二:升级SpaServices.Extensions包
确保项目中使用的Microsoft.AspNetCore.SpaServices.Extensions包是最新版本,新版本可能已修复Vite日志识别的问题。在NuGet包管理器中更新该包至最新稳定版。
方法三:自定义日志检测逻辑(进阶)
如果需要保留自动端口检测,可以自定义ReactDevelopmentServerMiddleware的日志解析逻辑,让其识别Vite的"ready"日志。但该方法需要编写自定义中间件,复杂度较高,推荐优先使用方法一。
内容的提问来源于stack exchange,提问作者Ramesh Sivaraman

