You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET5升级.NET8合并Startup与Program后Angular请求404

ASP.NET Core Angular升级.NET 8后API返回404问题排查

背景

为统一项目基础设施、复用通用代码(如日志配置),我将多个项目统一为相同.NET版本,其中2个基于Angular模板、2个基于Blazor模板,该方案可行。

随后将ASP.NET Core Angular应用从.NET 5升级至.NET 8,合并了Startup.cs与Program.cs,并参照.NET 8 Blazor应用配置添加了Serilog。应用可正常启动,但客户端Angular请求控制器API时返回HTTP 404错误。

相关代码

更新后的Program.cs

var builder = WebApplication.CreateBuilder(args);

var databaseConfig = builder.Configuration.GetSection("Database").Get<DatabaseConfig>();

builder.Services.AddControllers(options =>
{
    options.Filters.Add(new SessionIdFilter());
}).AddJsonOptions(opts => opts.JsonSerializerOptions.PropertyNamingPolicy = null);

// 生产环境下,Angular文件将从此目录提供
builder.Services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; });

builder.Services.AddSingleton(databaseConfig);

builder.Services.AddDbContext<NumberDataContext>(options =>
{
    options.UseSqlServer(databaseConfig.ConnectionString);
});

builder.Services.AddSession(options => {
    options.Cookie.Name = "SID";
    options.IdleTimeout = TimeSpan.FromDays(30);
});

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = _ => false;
});

builder.Services.AddMvc(setup => {
}).AddFluentValidation();

builder.Services.AddTransient<IValidator<UpdateEndedModel>, UpdateEndedModelValidator>();

builder.Services.AddScoped<ICheckService, CheckService>();
builder.Services.AddScoped<IGameService, GameService>();
builder.Services.AddScoped<ITutorialService, TutorialService>();

var logger = new LoggerConfiguration()
    .ReadFrom.Configuration(builder.Configuration)
    .Enrich.FromLogContext()
    .CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);
builder.Host.UseSerilog(logger);

var app = builder.Build();

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

app.UseSession();

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

if (!app.Environment.IsDevelopment())
{
    app.UseSpaStaticFiles();
}

app.UseRouting();

app.UseSpa(spa =>
{
    spa.Options.SourcePath = "ClientApp";

    if (app.Environment.IsDevelopment())
    {
        spa.UseAngularCliServer(npmScript: "start");
    }
});

app.MapControllers();

app.Run();

合并时移除的原代码

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });

问题原因

核心问题是中间件执行顺序错误:在.NET 6+的最小API模型中,UseSpa()中间件会捕获所有未匹配的请求并转发至SPA应用。当前代码中UseSpa()放在MapControllers()之前,导致控制器路由还未被匹配就被SPA中间件拦截,最终返回404。

解决方案

调整中间件顺序,将MapControllers()移至UseSpa()之前,确保路由先匹配控制器API:

app.UseRouting();

// 优先映射控制器路由,避免被SPA中间件拦截
app.MapControllers();

app.UseSpa(spa =>
{
    spa.Options.SourcePath = "ClientApp";

    if (app.Environment.IsDevelopment())
    {
        spa.UseAngularCliServer(npmScript: "start");
    }
});

额外检查项

  • 确认控制器类上已添加[ApiController]和[Route]属性,路由配置正确
  • 开发环境下检查Angular的proxy.conf.json,确保API请求被正确转发至后端
  • 验证builder.Services.AddControllers()和app.MapControllers()均已正确配置,无遗漏

内容的提问来源于stack exchange,提问作者user571203

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:35:04