ASP.NET Core中预检请求与实际请求一致引发数据异常求助
解决ASP.NET Core跨域预检请求被转为POST的问题
一、问题根源
浏览器发送的OPTIONS预检请求到后端变成POST,基本都是反向代理(Nginx、IIS这类)配置出错——代理没保留原始请求方法,或者把OPTIONS请求错误转成了POST,导致后端无法区分预检和实际请求,进而触发两次API调用。
二、具体解决步骤
1. 修正反向代理配置
Nginx场景:
直接在配置里处理OPTIONS预检请求,同时转发时保留原始请求方法:
server { # 其他原有配置... # 直接处理OPTIONS预检,返回204响应 if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS'; add_header Access-Control-Allow-Headers 'Content-Type, X-Request-ID, X-Real-IP, Authorization'; add_header Access-Control-Allow-Credentials 'true'; add_header Content-Length 0; add_header Content-Type text/plain; return 204; } # 转发请求到ASP.NET Core服务,必须保留原始请求方法 location / { proxy_pass http://你的ASP.NET Core服务地址; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关键:传递原始请求方法 proxy_set_header Request-Method $request_method; } }
IIS场景:
检查URL重写规则是否篡改了OPTIONS请求,同时在web.config里配置预检响应:
<system.webServer> <rewrite> <rules> <rule name="转发到ASP.NET Core" stopProcessing="true"> <match url="(.*)" /> <action type="Rewrite" url="http://你的ASP.NET Core服务地址/{R:1}" /> <!-- 确保不对OPTIONS请求做额外改写 --> <conditions> <add input="{REQUEST_METHOD}" pattern="OPTIONS" negate="false" /> </conditions> </rule> </rules> </rewrite> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /> <add name="Access-Control-Allow-Headers" value="Content-Type, X-Request-ID, X-Real-IP, Authorization" /> <add name="Access-Control-Allow-Credentials" value="true" /> </customHeaders> </httpProtocol> </system.webServer>
2. 调整ASP.NET Core中间件顺序
CORS中间件必须放在路由和授权中间件之前,不然没法提前拦截预检请求:
var builder = WebApplication.CreateBuilder(args); // 注册CORS策略 builder.Services.AddCors(cors => { cors.AddPolicy("bundle_cors", builder => { builder .AllowAnyHeader() .AllowAnyMethod() .WithExposedHeaders("set-cookie") .AllowCredentials() .SetIsOriginAllowed(_ => true); }); }); builder.Services.AddControllers(); var app = builder.Build(); // 关键:UseCors要放在UseRouting和UseAuthorization前面 app.UseCors("bundle_cors"); app.UseRouting(); app.UseAuthorization(); app.MapControllers(); app.Run();
3. 控制器层临时过滤(应急方案)
如果暂时改不了代理配置,就在控制器加个全局过滤器,直接拦截OPTIONS请求:
// 全局CORS预检过滤器 public class CorsPreflightFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { if (context.HttpContext.Request.Method == HttpMethod.Options.Method) { var origin = context.HttpContext.Request.Headers["Origin"].ToString(); context.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", origin); context.HttpContext.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); context.HttpContext.Response.Headers.Add("Access-Control-Allow-Headers", context.HttpContext.Request.Headers["Access-Control-Request-Headers"]); context.HttpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true"); context.HttpContext.Response.StatusCode = StatusCodes.Status204NoContent; context.Result = new EmptyResult(); } } public void OnActionExecuted(ActionExecutedContext context) { } } // 在Program.cs里注册过滤器 builder.Services.AddControllers(options => { options.Filters.Add<CorsPreflightFilter>(); });
三、验证方法
- 绕开代理,用Postman直接给ASP.NET Core服务发OPTIONS请求,确认后端能收到OPTIONS方法并返回204。
- 再用浏览器发起请求,看网络面板里OPTIONS请求返回204,实际POST请求只发一次。
内容的提问来源于stack exchange,提问作者Rustam Salakhutdinov
相关产品推荐
相关产品推荐

