Maison > Article > interface Web > À propos du problème des cookies lors de la synchronisation d'Ajax
Il est vraiment impuissant de rencontrer ce genre de problème. La compatibilité des navigateurs frontaux a toujours été un casse-tête.
Cet article n'enregistre qu'une journée aussi embarrassante et impuissante . Utilisez-le pour soulager l'ennui de tout le monde T_T
Collègue : Allez ! Allez! Quelque chose s'est mal passé en ligne ! !
Moi : Wow ?! Quoi ?! QUOI ?! Nana ?!
Collègue : Est-ce causé par cette libération ?
Moi : Reculez ! retour en arrière ! (Pourquoi tu t'emportes quand tu es sur le point de manger ! Ne t'inquiète pas pour ton estomac ! Vérifiez-le vite)
...
Je ne peux me calmer qu'après un conversation déroutante." Démineur".
Rollback, proxy, capture de paquets, comparaison, dépannage à un seul facteur. . .
Après avoir terminé une série de coups de poing combo, il a fallu environ un bâton d'encens pour enfin trouver la faille. Il s'est avéré que c'était un problème avec le rappel de synchronisation ajax ! Déraisonnable! Cela ne devrait pas être le cas ! Existe-t-il une telle opération ? !
Utilisez ajax pour faire une requête "Synchronisation" Cette requête. renverra un cookie
success
Échec de la lecture de ce cookie cible dans le rappel !document.cookie
ne sera pas mis à jour tant que l'exécution ajax n'est pas terminée
La portée d'influence sur PC et Android est petite et occasionnelle.
IOS est le domaine le plus durement touché. La plupart des navigateurs autres que Chrome et Safari auront ce problème, et l'environnement Webview intégré de l'application n'est pas non plus à l'abri de ce problème.
La prélecture du cookie renvoyé par cette requête dans ce rappel de requête synchrone entraînera des problèmes.
La moitié du pays est tombée, à quoi me sert cette barre de fer !
Je peux vous pardonner le problème de compatibilité à petite échelle, mais vous êtes tellement arrogant, comment pouvez-vous le cacher !
Pour éliminer certains éléments d'interférence et restaurer son essence, nous utilisons les frameworks nej
, jQuery
et js
pour écrire plusieurs "synchronisations" avec le même fonction. Démo, attendez et voyez. .
[nej.html] Utilisez la bibliothèque NEJ
<!DOCTYPE html> <html> <head> <title>nej</title> <meta charset="utf-8" /> </head> <body> test <script src="http://nej.netease.com/nej/src/define.js?pro=./"></script> <script> define([ '{lib}util/ajax/xdr.js' ], function () { var _j = NEJ.P('nej.j'); _j._$request('/api', { sync: true, method: 'POST', onload: function (_data) { alert("cookie:\n" + document.cookie) } }); }); </script> </body> </html>
[jquery.html] Utilisez la bibliothèque jQuery
<!DOCTYPE html> <html> <head> <title>jquery</title> <meta charset="utf-8" /> </head> <body> jquery <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script> $.ajax({ url: '/api', async: false, method: 'POST', success: function (result) { alert("cookie:\n" + document.cookie) } }); </script> </body> </html>
[js.html] Implémentez la vôtre requête ajax Les fonctions
<!DOCTYPE html> <html> <head> <title>JS</title> <meta charset="utf-8" /> </head> <body> js <script> var _$ajax = (function () { /** * 生产XHR兼容IE6 */ var createXHR = function () { if (typeof XMLHttpRequest != "undefined") { // 非IE6浏览器 return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined") { // IE6浏览器 var version = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", ]; for (var i = 0; i < version.length; i++) { try { return new ActiveXObject(version[i]); } catch (e) { return null } } } else { throw new Error("您的系统或浏览器不支持XHR对象!"); } }; /** * 将JSON格式转化为字符串 */ var formatParams = function (data) { var arr = []; for (var name in data) { arr.push(name + "=" + data[name]); } arr.push("nocache=" + new Date().getTime()); return arr.join("&"); }; /** * 字符串转换为JSON对象,兼容IE6 */ var _getJson = (function () { var e = function (e) { try { return new Function("return " + e)() } catch (n) { return null } }; return function (n) { if ("string" != typeof n) return n; try { if (window.JSON && JSON.parse) return JSON.parse(n) } catch (t) { } return e(n) }; })(); /** * 回调函数 */ var callBack = function (xhr, options) { if (xhr.readyState == 4 && !options.requestDone) { var status = xhr.status; if (status >= 200 && status < 300) { options.success && options.success(_getJson(xhr.responseText)); } else { options.error && options.error(); } //清空状态 this.xhr = null; clearTimeout(options.reqTimeout); } else if (!options.requestDone) { //设置超时 if (!options.reqTimeout) { options.reqTimeout = setTimeout(function () { options.requestDone = true; !!this.xhr && this.xhr.abort(); clearTimeout(options.reqTimeout); }, !options.timeout ? 5000 : options.timeout); } } }; return function (options) { options = options || {}; options.requestDone = false; options.type = (options.type || "GET").toUpperCase(); options.dataType = options.dataType || "json"; options.contentType = options.contentType || "application/x-www-form-urlencoded"; options.async = options.async; var params = options.data; //创建 - 第一步 var xhr = createXHR(); //接收 - 第三步 xhr.onreadystatechange = function () { callBack(xhr, options); }; //连接 和 发送 - 第二步 if (options.type == "GET") { params = formatParams(params); xhr.open("GET", options.url + "?" + params, options.async); xhr.send(null); } else if (options.type == "POST") { xhr.open("POST", options.url, options.async); //设置表单提交时的内容类型 xhr.setRequestHeader("Content-Type", options.contentType); xhr.send(params); } } })(); _$ajax({ url: '/api', async: false, type: 'POST', success: function (result) { alert("cookie:\n" + document.cookie) } }); </script> </body> </html>
sont les mêmes dans les trois fichiers. Une fois le code HTML chargé, une requête synchrone est lancée. La requête renverra un cookie, document.cookie
sera imprimé. out pour détecter si le rappel est déjà en cours. Le cookie est écrit à ce moment-là.
Ce qui suit utilise un nœud pour implémenter ce service de cookies inscriptibles.
【serve.js】
var express = require("express"); var http = require("http"); var fs = require("fs"); var app = express(); var router = express.Router(); router.post('/api', function (req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With"); res.header("Set-Cookie", ["target=ccccccc|" + new Date()]); res.end('ok'); }); router.get('/test1', function (req, res, next) { fs.readFile("./nej.html", function (err, data) { res.end(data); }); }); router.get('/test2', function (req, res, next) { fs.readFile("./jquery.html", function (err, data) { res.end(data); }); }); router.get('/test3', function (req, res, next) { fs.readFile("./js.html", function (err, data) { res.end(data); }); }); app.use('/', router); http.createServer(app).listen(3000);
D'accord, tout va bien, lançons
$ node serve.js
Nous effectuons les opérations suivantes dans l'ordre,
Utilisez le navigateur iOS QQ et effacez tous les caches
Chargez l'une des pages et observez s'il y a une sortie de cookie cible
Effectuez une opération d'actualisation, observez s'il existe une sortie de cookie cible, comparez l'horodatage de la sortie du cookie et confirmez s'il s'agit du résultat de synchronisation du dernier cookie plutôt que du cookie obtenu par cette requête. ,
Effacez tous les caches, changez de fichier HTML cible et exécutez les étapes 2, 3 et 4 en boucle
[nej.html]
Chargement de l'environnement pur, le cookie cible n'est pas lu
Actualiser le chargement, le cookie renvoyé par la requête précédente est lu
[jquery.html]
Chargement de l'environnement pur, le cookie cible n'est pas lu
Chargement de l'actualisation, le cookie cible n'est pas lu
[js.html]
Chargement de l'environnement pur, le le cookie cible n'est pas lu
Actualisation du chargement, le cookie cible n'est pas lu
Hein ? Le résultat est différent ! Le deuxième chargement utilisant nej lit le premier cookie. Les deux autres temps ont été obtenus.
nej Le chargement du framework dépendant est asynchrone Lorsque la requête synchrone est lancée, le dom a été chargé Lorsque le rappel répond, document.cookie
est déjà dans le "prêt". " état et est lisible et écrit. Mais la requête ne peut toujours pas obtenir le cookie renvoyé par elle-même.
Les deux autres mécanismes de chargement bloquent le chargement du dom, empêchant le dom d'être chargé lorsque la demande de synchronisation est lancée lorsque le rappel répond. , document.cookie
Toujours pas inscriptible.
Nous modifierons la logique des fichiers html ci-dessus.
Retardez la demande de synchronisation jusqu'à ce que le document soit cliqué.
Ce qui suit
$('document').click(function () { // TODO 发起同步请求 });
sont toujours les étapes d'exécution ci-dessus, jetons un coup d'œil aux résultats
[nej.html]
Chargement en environnement pur, le cookie cible n'est pas lu
Actualiser le chargement, le cookie renvoyé par la requête précédente est lu
【jquery.html】
Chargement de l'environnement pur, le cookie cible n'est pas lu
Actualiser le chargement, la dernière fois est lu Le cookie renvoyé par la requête
[js.html]
est chargé dans un environnement propre et le cookie cible n'est pas lu
刷新加载,读取到上一次请求返回的 cookie
结果和预期一样,本次请求无法获取本期返回的目标 cookie,请求回调执行后,目标cookie才会更新到document.cookie
上。
在执行以上操作是,发现,【jquery.html】的执行结果时不时会有两种结果
纯净环境加载,未读取到目标 cookie
刷新加载,读取到上一次请求返回的 cookie
另外一种几率较小,但也会出现
纯净环境加载,读取到目标 cookie
刷新加载,读取到目标 cookie
一言不合看源码
我们在 jquery 的源码中看到,jquery 的success
回调绑定在了 onload
事件上
https://code.jquery.com/jquery-3.2.1.js :9533行
而我自己实现的和 nej 的实现均是将success
回调绑定在了 onreadystatechange
事件上,唯一的区别就在于此
一个正向的 ajax 请求,会先触发两次onreadystatechange
,在触发onload
,或许原因在于document.cookie
的同步有几率在onload
事件触发前完成??I'm not sure.
在 PC 端,Android 端,IOS 端Chrome、Safari 浏览器环境下,ajax 的同步请求的回调方法中,取到本请求返回的 cookie 失败几率低
IOS 端,QQ 浏览器、App 内置Webview浏览器环境下,失败率极高。
只有问题没有方案的都是在耍流氓!
将回调方法中的 cookie 获取方法转化为异步操作。
_$ajax({ url: '/api', async: false, type: 'POST', success: function (result) { setTimeout(function(){ // do something 在此处获取 cookie 操作是安全的 },0) } });
没有把握的方案,我们是要斟酌着实施的。
如果你不能100%却被操作的安全性,那并不建议你强行使用 ajax 的同步操作,很多机制并不会像我们自以为是的那样理所应当。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!