为何跨源资源共享(CORS)未在服务器端拦截请求?附场景验证
为什么CORS没有在服务器端拦截POST请求?
这是因为你的POST请求属于CORS简单请求,浏览器不会先发送OPTIONS预检请求,而是直接发送实际的POST请求。服务端会先执行你的Post()方法,之后才会通过CORS中间件检查源是否合法——当发现源不被允许时,服务端不会添加CORS允许的响应头,但此时控制器方法已经执行完毕了。
详细解释:
CORS请求分为两类:
简单请求:满足以下全部条件的请求:
- HTTP方法是GET、HEAD或POST
- Content-Type仅限于
application/x-www-form-urlencoded、multipart/form-data或text/plain - 没有自定义的HTTP请求头
你的$.post调用默认使用application/x-www-form-urlencoded作为Content-Type,完全符合简单请求的标准。
预检请求:不满足简单请求条件的请求,浏览器会先发送OPTIONS请求到服务端,询问是否允许跨源请求。服务端的CORS中间件会直接处理这个OPTIONS请求,如果源不被允许,会直接返回403,不会执行后续的控制器方法。
对于简单请求,浏览器的处理流程是:
- 直接发送实际的请求(比如你的POST)到服务端
- 服务端执行控制器方法
- 服务端通过CORS中间件检查请求源是否在允许列表中
- 如果源不被允许,服务端不会添加
Access-Control-Allow-Origin等响应头 - 浏览器收到响应后,发现没有允许当前源的CORS头,就会触发
fail回调,但此时服务端的方法已经执行过了。
验证一下:
你可以给Get()方法也加个断点,会发现点击GET按钮时,Get()同样会被执行,然后浏览器才会报错——因为GET也是简单请求,流程和POST完全一致。
如何让服务端在执行方法前拦截?
如果希望服务端在执行控制器方法前就拦截不符合CORS规则的请求,你需要让请求变成预检请求。最简单的方式是修改客户端的POST请求,设置Content-Type为application/json:
$('#sender-post').click(() => { $.ajax({ url: "http://localhost:9000/weatherforecast", type: "POST", contentType: "application/json", data: JSON.stringify({}), // 需要传递JSON格式的参数 success: () => { $('#content-post').text('done'); }, error: () => { $('#content-post').text('fail'); } }); });
这样浏览器会先发送OPTIONS预检请求,服务端的CORS中间件会直接检查源是否允许(你的配置只允许http://localhost:5000),发现不匹配后直接返回403,不会执行Post()方法。
另外,顺便提一下:你的Startup中UseCors()的位置是正确的,但如果需要允许特定的HTTP方法或头,可以在策略中明确配置:
options.AddDefaultPolicy(policy => { policy.WithOrigins("http://localhost:5000") .WithMethods("GET", "POST"); // 明确指定允许的HTTP方法 });
内容的提问来源于stack exchange,提问作者jysummers
相关产品推荐
相关产品推荐

