环境:
打包工具: Webpack;
整合软件包: WAMP;
编辑器:VsCode;
Webpack虚拟出的端口号是8080,本地Apache的端口号是80
问题重现:
JS代码,使用FormData对象作为传输数据的格式:
function postData() { var formData = new FormData(); formData.append("data", JSON.stringify({name:‘xxx‘, age:20})); function xhrRequest(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open(‘POST‘, _this.gRequestPhpUrl); xhr.setRequestHeader("cache-control","no-cache"); //② xhr.onreadystatechange = function() { if (this.readyState === 4 && this.status === 200){ var dataResp = JSON.parse(this.responseText); resolve(dataResp, this); } else { reject(‘网络错误:‘ + this.status, this); } } xhr.send(formData); } var sendReq = new Promise(xhrRequest); return sendReq; }
PHP代码,使用跨域接收他域传来的访问请求:
<?php header(‘Access-Control-Allow-Origin:*‘); header(‘Access-Control-Allow-Headers: cache-control‘); //① if ( !isset($_POST[‘data‘])){ return; } file_put_contents(‘./rec.txt‘, json_encdoe($_POST[‘data‘])); $objResp = new class{}; $objResp->data = ‘this is from server‘; echo json_encode($objResp); ?>
问题:
XMLHttpRequest请求代码在Webpack虚拟的环境中发出,请求PHP文本资源。调试PHP时发现JS发起请求后,$_POST数组处于—— 一次请求数组为空、下一次请求数组有元素、再下一次请求数组又为空,这样有序的循环中。且每次请求file_get_contents("php://input", "r")中也没有值。
奇怪的是当$_POST数组为空时前端竟然收到了正确的JSON格式消息。
问题排查:
觉得奇怪的是前台用FormData包裹的数据,后台$_POST数组却为空,且从‘php://input’中读不到值。
1. 怀疑JS发起请求的请求头‘Content-Type‘值不对。Server通过这个字段得到请求头的类型,然后进行解析。[1] 如果解析不对,$_POST数据将出错或没有数据。 ——》 尝试各种’Content-Type‘,没有解决这个问题;
2. 注意到浏览器控制台有OPTIONS类型的请求,即某次请求发出,控制台出现一次OPTIONS请求,
发现该请求里没有请求数据,此时调试PHP,发现$_POST为空的情况。随即在没有手动发送请求情况下控制台自动发出一次POST请求,这时请求里包含请求数据。
解决方案:
不让浏览器发送OPTIONS请求,而是只发送一次POST请求。
查阅资料后了解OPTIONS请求是一个预检请求,xhr判断请求头里的URL路径是否能被访问到,若不能的话就返回一个CORS策略问题(
上图是注释掉①处代码会出现的问题,因为跨域时PHP需要设置能够接收包含’cache-control‘的请求头),若可以的话就会再次发送原来的POST请求。
如果在跨域时不想要发送OPTIONS请求,需满足下列条件:[2]
1. 不要发送下列方法的请求:
PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
2. 除请求头自带的设置,不要手动设置除下列的其它字段:
Accept、Accept-Language、Content-Language、Content-Type (but note the additional requirements below)、DPR、Downlink、Save-Data、Viewport-Width、Width
3. 上述2中的’Content-Type‘字段不可以设置除以下的值:
application/x-www-form-urlencoded、multipart/form-data、text/plain
综上所述,只要把代码中的①和②处注释掉就可以了。
参考:
[1] https://blog.csdn.net/qq_27845259/article/details/83106391 —— ’Content-Type‘字段类型及简介
[2] https://juejin.im/entry/58eaf351a22b9d0058a8e35c —— 浅谈 AJAX 跨域请求时的 OPTIONS 方法
https://segmentfault.com/a/1190000016040998 —— 发送两次请求,其中有个是OPTIONS请求
原文地址:https://www.cnblogs.com/yichi/p/11014267.html