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

React与ASP.NET Core WebAPI间歇性CORS问题排查求助

React + ASP.NET Core WebAPI 间歇性CORS问题排查与解决

问题现象

我有React前端和ASP.NET Core WebAPI后端,已在WebAPI的Startup.cs中配置CORS,首次加载页面时可正常与API交互,但存在以下间歇性问题:

  • 多标签页打开网站、会话超时或执行登出操作时,会触发CORS错误
  • 错误提示:Access to fetch at ‘/api/lastvisited' from origin 'https://localhost.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
  • 请求报错:POST https://localhost/api/v1/lastvisited net::ERR_FAILED 302 (anonymous)
  • 代码报错:Uncaught (in promise) TypeError: Failed to fetch

相关代码

WebAPI Program.cs

namespace WebAPI
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
 public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

WebAPI Startup.cs

namespace WebAPI
{
    public class Startup
    {
      public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
        {
            Configuration = configuration;
            WebHostEnvironment = webHostEnvironment;
        }
        public IConfiguration Configuration { get; }
        public IWebHostEnvironment WebHostEnvironment { get; }
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddCors();
            services.AddHttpContextAccessor();
            services.AddScoped<HttpContextAccessor>();
        }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime appLifetime)
        {            AppHttpContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
            //Allow cors domain
            string[] CORSAllowedDomains = Configuration.GetSection("CORSAllowedDomains").Get<string[]>();
            app.UseCors(
               options => options.WithOrigins(
               CORSAllowedDomains).WithMethods("PUT", "DELETE", "GET", "POST").AllowAnyMethod().AllowAnyHeader().AllowCredentials().SetIsOriginAllowedToAllowWildcardSubdomains()          );
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            if (!WebHostEnvironment.IsEnvironment("Local"))
            {
                app.UseForwardedHeaders();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseEndpoints(endpoints => endpoints.MapControllers());
        }
    }
}

React Header组件代码

export class Header extends React.Component {
  constructor(props) {
    this.state = {
      loading: true
    };
  }

  onLogout() {
    const d = new Date();
    let lastVisitedDateTime = d.toLocaleString();
    lastVisitedDateTime = lastVisitedDateTime.replace(",", "");
    const data = {
      lastVisitedDateTime
    };
    const SERVICE_URL = "/lastvisiteddateandtime";
         fetch(SERVICE_URL, {
         method: "POST",
         headers: { "Content-Type": "application/json" },
         credentials: "include",
        body: JSON.stringify(data)
    })
      .then((response) => response.json())
      .catch((error) => console.error("Error:", error));
 window.sessionStorage.clear();
 window.location.href = "/";
  }
  render() {
    return (
                <Link         
                  onClick={this.onLogout.bind(this)}>
                  Logout</Link>
      </div>
    );
  }
}

export default Header;

解决方案

1. 修正中间件执行顺序

ASP.NET Core中间件顺序直接影响CORS头的生效时机,UseCors必须放在UseRouting之前,且早于UseHttpsRedirection、UseStaticFiles等中间件。同时移除重复的WithMethods配置:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime appLifetime)
{
    AppHttpContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
    
    // 优先配置CORS,确保在路由处理前生效
    string[] CORSAllowedDomains = Configuration.GetSection("CORSAllowedDomains").Get<string[]>();
    app.UseCors(options => options
        .WithOrigins(CORSAllowedDomains)
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials()
        .SetIsOriginAllowedToAllowWildcardSubdomains());
    
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    
    if (!WebHostEnvironment.IsEnvironment("Local"))
    {
        app.UseForwardedHeaders();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseEndpoints(endpoints => endpoints.MapControllers());
}

2. 处理302重定向导致的CORS失效

错误中的302状态码说明会话超时后API自动重定向到登录页,而重定向后的地址不在CORS允许列表中,导致报错。需修改API返回401而非302:

  • 添加全局授权过滤器:
public class NoRedirectAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        if (!context.HttpContext.User.Identity.IsAuthenticated)
        {
            context.Result = new UnauthorizedResult(); // 返回401状态码,避免重定向
        }
    }
}

// 在ConfigureServices中注册过滤器
services.AddControllers(options =>
{
    options.Filters.Add<NoRedirectAuthorizationFilter>();
});
  • 前端处理401错误:
fetch(SERVICE_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    credentials: "include",
    body: JSON.stringify(data)
})
.then((response) => {
    if (!response.ok) {
        if (response.status === 401) {
            window.sessionStorage.clear();
            window.location.href = "/";
        }
        throw new Error('请求失败');
    }
    return response.json();
})
.catch((error) => console.error("Error:", error));

3. 确保登出请求完成后再跳转

当前登出逻辑中,fetch异步请求未完成就执行页面跳转,导致请求中断触发异常。修改为异步等待请求完成:

async onLogout() {
    const d = new Date();
    let lastVisitedDateTime = d.toLocaleString().replace(",", "");
    const data = { lastVisitedDateTime };
    const SERVICE_URL = "/lastvisiteddateandtime";
    
    try {
        await fetch(SERVICE_URL, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            credentials: "include",
            body: JSON.stringify(data)
        });
    } catch (error) {
        console.error("Error:", error);
    } finally {
        window.sessionStorage.clear();
        window.location.href = "/";
    }
}

4. 验证CORS域名配置

确保CORSAllowedDomains中的域名与前端实际Origin完全匹配,包括协议(http/https)、域名、端口,例如前端是https://localhost.net,配置中需完整填写该地址。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 01:20:42