ASP.NET Core 6 MVC带授权策略的HttpPost请求出现CORS错误
你遇到的问题核心是:POST请求因带自定义头或非简单Content-Type触发了预检OPTIONS请求,但服务端没正确返回CORS相关响应头;而GET属于浏览器定义的「简单请求」,不需要预检所以能正常执行。下面是针对性的排查和解决步骤:
1. 修正Program.cs中CORS中间件的位置与策略
ASP.NET Core中间件的顺序至关重要,UseCors必须放在UseRouting之后、UseAuthorization之前,同时要覆盖预检请求需要的所有规则:
var builder = WebApplication.CreateBuilder(args); // 注册CORS策略 builder.Services.AddCors(options => { options.AddPolicy("AllowAppOrigin", policy => { // 替换成你的应用实际域名(比如http://your-iis-domain.com) policy.WithOrigins("http://your-app-domain.com") .AllowAnyMethod() // 允许所有HTTP方法(含OPTIONS) .AllowAnyHeader() // 允许所有请求头(覆盖自定义头导致的预检失败) .AllowCredentials(); // 登录后带Cookie的请求必须加这个 }); }); builder.Services.AddControllersWithViews(); // 其他服务注册(比如Azure AD身份验证相关) var app = builder.Build(); // 中间件顺序严格遵循以下顺序 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 此处必须放在UseRouting之后、UseAuthorization之前 app.UseCors("AllowAppOrigin"); app.UseAuthentication(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
注意:如果前端fetch请求带了credentials: 'include'(Azure登录后需要携带Cookie),CORS策略必须加上.AllowCredentials(),同时前端fetch要配置该选项。
2. 确保OPTIONS请求完全交给ASP.NET Core处理(IIS配置)
IIS 8.5默认的Handler Mapping会拦截OPTIONS请求,导致ASP.NET Core根本接收不到预检请求,需做以下配置:
方式一:修改web.config
在<system.webServer>节点下添加/修改:
<handlers> <!-- 移除默认拦截OPTIONS的处理器 --> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <!-- 让ASP.NET Core模块处理所有请求 --> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=".\YourApp.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" /> <!-- 不要手动添加Access-Control-Allow-Origin,避免和ASP.NET Core的CORS中间件冲突 --> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol>
方式二:通过IIS管理器配置
打开IIS管理器,找到你的站点,进入Handler Mappings:
- 删除
OPTIONSVerbHandler和ExtensionlessUrlHandler-Integrated-4.0 - 确认
aspNetCore处理器的路径为*、动词为*,确保所有请求(含OPTIONS)都交给ASP.NET Core处理
3. 检查前端fetch请求的配置
如果POST请求带了自定义头(比如Authorization)或Content-Type为application/json,需确保fetch配置正确:
fetch('/YourController/YourAction', { method: 'POST', headers: { 'Content-Type': 'application/json', // 其他自定义请求头 }, credentials: 'include', // 携带登录Cookie必须加这个 body: JSON.stringify(yourData) })
注:application/json类型的请求会触发预检,服务端CORS策略必须允许该头(用.AllowAnyHeader()即可覆盖)。
4. 禁用IIS自带的CORS模块(若已安装)
如果你之前在IIS上安装了官方CORS模块,它会和ASP.NET Core的CORS中间件冲突,导致预检响应头被覆盖或缺失。可在IIS管理器中进入站点的CORS功能,选择Disable,或在web.config中移除相关配置。
5. 验证预检请求是否到达ASP.NET Core
在Program.cs中添加一个调试中间件,确认OPTIONS请求是否被服务端接收:
// 放在UseCors之前 app.Use(async (context, next) => { if (context.Request.Method == "OPTIONS") { // 打印日志,确认OPTIONS请求到达服务端 Console.WriteLine("收到OPTIONS预检请求"); } await next(); });
开启web.config中的stdoutLogEnabled="true",查看日志确认请求是否到达。如果没收到,说明IIS仍在拦截OPTIONS请求,需回到步骤2重新配置。
内容的提问来源于stack exchange,提问作者Francesco B.

