Heim > Artikel > Web-Frontend > Über das Cookie-Problem der Ajax-Synchronisierung
Es ist wirklich hilflos, auf ein solches Problem zu stoßen.
Dieser Artikel beschreibt nur einen so peinlichen und hilflosen Tag. Benutzen Sie es, um allen die Langeweile zu vertreiben T_T
Kollege: Komm schon! Aufleuchten! Im Internet ist ein Fehler aufgetreten! !
Ich: Wow?! WAS?!
Kollege: Liegt es an dieser Veröffentlichung?
Ich: Roll back! Rollback! (Warum verlierst du die Beherrschung, wenn du gerade essen willst? Mach dir keine Sorgen um deinen Magen! Schau es dir schnell an)
...
Ich kann mich erst nach einer Weile beruhigen verwirrendes Gespräch. „Minesweeper“.
Rollback, Proxy, Paketerfassung, Vergleich, Einzelfaktor-Fehlerbehebung. . .
Nachdem ich eine Reihe von Combo-Schlägen ausgeführt hatte, dauerte es etwa ein Räucherstäbchen, um den Fehler endlich zu finden. Es stellte sich heraus, dass es sich um ein Problem mit dem Ajax-Synchronisations-Callback handelte! Unvernünftig! Das sollte nicht sein! Gibt es eine solche Operation? !
Verwenden Sie Ajax, um eine „Synchronisierungsanfrage“ zu stellen gibt ein Cookie zurück.
success
Dieses Ziel-Cookie konnte im Rückruf nicht gelesen werden!document.cookie
wird erst aktualisiert, wenn die Ajax-Ausführung abgeschlossen ist
Der Einflussbereich auf PC und Android ist gering und gelegentlich.
IOS ist der am stärksten betroffene Bereich, außer Chrome und Safari, und auch die integrierte Webview-Umgebung der App ist nicht immun.
Das Vorablesen des von dieser Anfrage zurückgegebenen Cookies innerhalb dieses synchronen Anfragerückrufs führt zu Problemen.
Das halbe Land ist gefallen, welchen Nutzen habe ich mit dieser Eisenstange!
Das kleine Kompatibilitätsproblem kann ich dir verzeihen, aber du bist so arrogant, wie kannst du es verbergen!
Um einige Interferenzelemente zu beseitigen und ihr Wesen wiederherzustellen, verwenden wir die Frameworks nej
, jQuery
und js
, um mehrere „Synchronisationen“ mit dem zu schreiben Gleiche Funktion. Demo, abwarten. .
[nej.html] Verwenden Sie die NEJ-Bibliothek
<!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] Verwenden Sie die jQuery-Bibliothek
<!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] Implementieren Sie Ihre eigene Ajax-Anfrage Die Funktionen
<!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>
sind in den drei Dateien gleich. Nach dem Laden der HTML-Datei wird eine synchrone Anfrage initiiert. Im Rückruf wird document.cookie
ausgegeben um festzustellen, ob der Rückruf bereits ausgeführt wird. Das Cookie wird zu diesem Zeitpunkt geschrieben.
Im Folgenden wird Node verwendet, um diesen beschreibbaren Cookie-Dienst zu implementieren.
【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);
Okay, alles ist in Ordnung, lass uns
$ node serve.js
Wir führen die folgenden Operationen nacheinander aus,
Verwenden Sie den iOS-QQ-Browser und leeren Sie alle Caches
Laden Sie eine der Seiten und beobachten Sie, ob eine Ziel-Cookie-Ausgabe erfolgt
Führen Sie einen Aktualisierungsvorgang durch, beobachten Sie, ob eine Ziel-Cookie-Ausgabe vorliegt, vergleichen Sie den Zeitstempel der Cookie-Ausgabe und bestätigen Sie, ob es sich um das Synchronisierungsergebnis des letzten Cookies und nicht um das durch diese Anfrage erhaltene Cookie handelt ,
Alle Caches löschen, die Ziel-HTML-Datei wechseln und die Schritte 2, 3 und 4 in einer Schleife ausführen
[nej.html]
Reines Umgebungsladen, das Zielcookie wird nicht gelesen
Aktualisieren Sie den Ladevorgang, das Cookie wird zurückgegeben von der vorherigen Anfrage wird gelesen
[jquery.html]
Reines Umgebungsladen, das Ziel-Cookie wird nicht gelesen
Laden aktualisieren, das Ziel-Cookie wird nicht gelesen
[js.html]
Reines Umgebungsladen, das Ziel-Cookie wird nicht gelesen
Laden aktualisieren, Ziel-Cookie wird nicht gelesen
Häh? Das Ergebnis ist anders! Der zweite Ladevorgang mit nej liest das erste Cookie. Die anderen beiden Zeiten wurden erhalten.
nej Das Laden des abhängigen Frameworks erfolgt asynchron. Wenn die synchrone Anfrage initiiert wird, wurde der Dom geladen. Wenn der Rückruf antwortet, ist document.cookie
im Zustand „Bereit“. Zustand und ist lesbar und schreibbar. Die Anfrage kann das zurückgegebene Cookie jedoch immer noch nicht selbst abrufen.
Die anderen beiden Lademechanismen blockieren das Laden von Dom, was dazu führt, dass der Dom nicht geladen wird, wenn die Synchronisierungsanforderung initiiert wird, wenn der Rückruf antwortet , document.cookie
Immer noch nicht beschreibbar.
Wir werden die Logik der oben genannten HTML-Dateien ändern.
Verzögern Sie die Synchronisierungsanforderung, bis auf das Dokument geklickt wird.
Das Folgende
$('document').click(function () { // TODO 发起同步请求 });
sind immer noch die oben genannten Ausführungsschritte, werfen wir einen Blick auf die Ergebnisse
[nej.html]
Reines Laden der Umgebung, das Ziel-Cookie wird nicht gelesen
Aktualisieren Sie den Ladevorgang, das von der vorherigen Anfrage zurückgegebene Cookie wird gelesen
【jquery.html】
Reines Umgebungsladen, das Zielcookie wird nicht gelesen
Laden aktualisieren, das letzte Mal wird gelesen. Das von der Anfrage zurückgegebene Cookie
[js.html]
wird in eine saubere Umgebung geladen und das Zielcookie wird nicht gelesen
刷新加载,读取到上一次请求返回的 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 的同步操作,很多机制并不会像我们自以为是的那样理所应当。
Das obige ist der detaillierte Inhalt vonÜber das Cookie-Problem der Ajax-Synchronisierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!