.NET应用配置CORS后仍无法与React前端通信的跨域问题
首次使用微软技术栈构建后端服务时,出现后端无法与React前端正常通信的问题。通过Postman发起的HTTP请求可成功响应,已排除项目构建本身存在问题。参考教程及社区解决方案完成CORS配置后,浏览器控制台仍抛出CORS拦截错误,错误截图如下:
var myAllowSpecificOrigins = "_myAllowSpecificOrigins"; var builder = WebApplication.CreateBuilder(args); // Add services to the container. var Configuration = builder.Configuration; builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext<BlogContext>(options => { var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); }); builder.Services.AddCors(options => { options.AddPolicy(name: myAllowSpecificOrigins, builder => { builder.WithOrigins("http://localhost:3000") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); } ); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(Configuration.GetValue<string>("JWTSecretKey")) ) }; }); builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddSingleton<IAuthService>( new AuthService( Configuration.GetValue<string>("JWTSecretKey"), Configuration.GetValue<int>("JWTLifespan") ) ); builder.Services .AddMvc().AddNewtonsoftJson(options => { options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); options.SerializerSettings.Converters.Add(new StringEnumConverter()); }); var app = builder.Build(); app.UseCors(myAllowSpecificOrigins); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
- 将CORS配置为允许所有来源后,前端调用
localhost:5090地址的接口时出现如下错误:Access to XMLHttpRequest at 'http://localhost:5090/api/auth/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request,即CORS策略拦截了来自http://localhost:3000源对http://localhost:5090/api/auth/login的XMLHttpRequest请求,提示预检请求响应未通过访问控制校验,预检请求不允许重定向。 - 前端调用
localhost:7090地址时,后端直接抛出网络连接错误。
核心问题1:中间件执行顺序错误
现有代码中app.UseHttpsRedirection()放在CORS中间件之后,当前端发HTTP请求到5090端口时,浏览器自动发起的OPTIONS预检请求会先被HTTPS重定向中间件拦截,返回3xx跳转响应。而CORS规范明确要求预检请求不能返回重定向,直接触发拦截。
修复方式:调整中间件顺序,CORS中间件必须放在所有请求处理中间件的最靠前位置,同时补上缺失的UseAuthentication()中间件(现有代码只配置了授权中间件,没有认证中间件会导致JWT鉴权流程失效),修正后的中间件管道代码如下:
var app = builder.Build(); // CORS中间件必须放在最外层,优先处理跨域校验 app.UseCors(myAllowSpecificOrigins); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // 补全认证中间件,必须放在授权中间件之前 app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
核心问题2:CORS规则配置冲突
AllowCredentials()配置和AllowAnyOrigin()是互斥的,配置允许携带凭证时,必须明确指定允许的来源,不能放开所有源,保持原有WithOrigins("http://localhost:3000")的配置即可,不要修改为允许所有来源。
核心问题3:端口使用错误
7090端口连接失败是因为该端口不是项目实际监听的端口,打开项目下Properties/launchSettings.json文件,查看applicationUrl字段配置的实际HTTP/HTTPS监听端口,前端请求时使用对应正确的端口即可。如果使用HTTPS端口,注意本地开发环境要信任ASP.NET Core的开发证书,避免证书校验失败导致连接异常。
内容的提问来源于stack exchange,提问作者Mr. PJ

