Jadual Kandungan
前言
场景再现
问题复现
一句话概括问题
影响范围
追因溯果
纵向对比
操作
结果
原因
单因子对照
特例
产生原因
问题结论
解决方案
方案1 - 明修栈道暗度陈仓
方案2 - 不抵抗政策
Rumah hujung hadapan web tutorial js 关于同步 ajax 的 cookie问题

关于同步 ajax 的 cookie问题

Oct 24, 2017 am 09:41 AM
ajax cookie ie

前言

遇到这种问题实属无奈,前端的浏览器兼容性一直是一个让人头痛的问问题

仅以此文记录如此尴尬无奈的一天。拿来替大伙儿解闷T_T

场景再现

同事:快来!快来!线上出问题了!!
我:神马?! 咩?! WHAT?! なに?!
同事:是这次发布造成的吗?
我:回滚!回滚!(为什么要在快吃饭的时候掉链子!顾不上肚子了!快查吧)
......

一通混乱的对话后只能静下心来“扫雷”了。

回滚、代理、抓包、对比、单因子排查。。。

一套组合拳打完,大概一炷香的时间,终于找到了破绽,竟然是 ajax 同步回调的问题!不合理啊!不应该啊!还有这种操作?!

问题复现

一句话概括问题

使用 ajax 做“同步”请求,此请求会返回一个 cookie,在success回调中读取此目标cookie 失败!ajax执行结束后 document.cookie 才会被更新

影响范围

PC 端和 Android 端影响范围小,属于偶现。

IOS 端是重灾区,出来 Chrome 和 Safari 浏览器外的绝大多说浏览器都会出现此问题,并且 App 内置的 Webview 环境同样不能幸免。

在本同步请求回调内预读取本请求返回的 cookie 会产生问题。

半壁江山都沦陷了,我要这铁棒有何用!

追因溯果

小范围的兼容问题我姑且可以饶你,奈何你如此猖狂,怎能任你瞒天过海!

纵向对比

排除一些干扰项,还原其本质,我们分别用框架nej,jQueryjs写几个相同功能的“同步” demo,走着瞧着。。

【nej.html】使用 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([
            &#39;{lib}util/ajax/xdr.js&#39;
        ], function () {
            var _j = NEJ.P(&#39;nej.j&#39;);
            _j._$request(&#39;/api&#39;, {
                sync: true,
                method: &#39;POST&#39;,
                onload: function (_data) {
                    alert("cookie:\n" + document.cookie)
                }
            });
        });
    </script>
</body>
</html>
Salin selepas log masuk

【jquery.html】使用 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: &#39;/api&#39;,
            async: false,
            method: &#39;POST&#39;,
            success: function (result) {
                alert("cookie:\n" + document.cookie)
            }
        });
    </script>
</body>
</html>
Salin selepas log masuk

【js.html】自己实现的 ajax 请求函数

<!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: &#39;/api&#39;,
            async: false,
            type: &#39;POST&#39;,
            success: function (result) {
                alert("cookie:\n" + document.cookie)
            }
        });
    </script>
</body>
</html>
Salin selepas log masuk

三个文件都是一样的,在html 加载完之后发起一个同步请求,该请求会返回一个 cookie,在回调中将document.cookie打印出来,检测是否已经在回调时写入的了 cookie。

下面使用 node 实现这个可写 cookie 的服务。
【serve.js】

var express = require("express");
var http = require("http");
var fs = require("fs");
var app = express();

var router = express.Router();
router.post(&#39;/api&#39;, 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(&#39;ok&#39;);
});

router.get(&#39;/test1&#39;, function (req, res, next) {
    fs.readFile("./nej.html", function (err, data) {
        res.end(data);
    });
});

router.get(&#39;/test2&#39;, function (req, res, next) {
    fs.readFile("./jquery.html", function (err, data) {
        res.end(data);
    });
});

router.get(&#39;/test3&#39;, function (req, res, next) {
    fs.readFile("./js.html", function (err, data) {
        res.end(data);
    });
});

app.use(&#39;/&#39;, router);
http.createServer(app).listen(3000);
Salin selepas log masuk

好了,万事大吉,run 一把

$ node serve.js
Salin selepas log masuk

操作

我们依次执行如下操作,

  1. 使用 ios 端 QQ 浏览器,清空所有缓存

  2. 加载其中一个页面,观察是否有目标 cookie 输出

  3. 执行刷新操作,观察是否有目标 cookie 输出,比较 cookie 输出的时间戳,确认是否为上次 cookie 的同步结果而非本次请求获取的 cookie,

  4. 清空所有缓存,切换目标 html 文件,循环执行2,3,4步骤

结果

【nej.html】

  • 纯净环境加载,未读取到目标 cookie

  • 刷新加载,读取到上一次请求返回的 cookie

【jquery.html】

  • 纯净环境加载,未读取到目标 cookie

  • 刷新加载,未读取到目标 cookie

【js.html】

  • 纯净环境加载,未读取到目标 cookie

  • 刷新加载,未读取到目标 cookie

咦?结果不一样!使用 nej 的第二次加载读取到了第一次 cookie。其他的两次均为获取到。

原因

nej 依赖框架的加载是异步的,当同步请求发起时,dom 已经加载完毕,回调相应时,document.cookie已经呈“ready”状态,可读可写。但请求依然获取不到自身返回携带的 cookie。

关于同步 ajax 的 cookie问题

而其他两种加载的机制阻塞了 dom 的加载,导致同步请求发起时,dom 尚未加载完成,回调相应时,document.cookie依然不可写。

单因子对照

我们将以上几个 html 文件的逻辑做下修改。
将同步请求推迟到 document 点击触发时再发起。
如下

$(&#39;document&#39;).click(function () {
    // TODO 发起同步请求
});
Salin selepas log masuk

依然是上面的执行步骤,来看看此次的结果

结果

【nej.html】

  • 纯净环境加载,未读取到目标 cookie

  • 刷新加载,读取到上一次请求返回的 cookie

【jquery.html】

  • 纯净环境加载,未读取到目标 cookie

  • 刷新加载,读取到上一次请求返回的 cookie

【js.html】

  • 纯净环境加载,未读取到目标 cookie

  • 刷新加载,读取到上一次请求返回的 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行

关于同步 ajax 的 cookie问题

而我自己实现的和 nej 的实现均是将success回调绑定在了 onreadystatechange 事件上,唯一的区别就在于此

一个正向的 ajax 请求,会先触发两次onreadystatechange,在触发onload,或许原因在于document.cookie的同步有几率在onload事件触发前完成??I'm not sure.

问题结论

  1. 在 PC 端,Android 端,IOS 端Chrome、Safari 浏览器环境下,ajax 的同步请求的回调方法中,取到本请求返回的 cookie 失败几率低

  2. IOS 端,QQ 浏览器、App 内置Webview浏览器环境下,失败率极高。

解决方案

只有问题没有方案的都是在耍流氓!

方案1 - 明修栈道暗度陈仓

将回调方法中的 cookie 获取方法转化为异步操作。

_$ajax({
    url: &#39;/api&#39;,
    async: false,
    type: &#39;POST&#39;,
    success: function (result) {
        setTimeout(function(){
            // do something 在此处获取 cookie 操作是安全的
        },0)
    }
});
Salin selepas log masuk

方案2 - 不抵抗政策

没有把握的方案,我们是要斟酌着实施的。

如果你不能100%却被操作的安全性,那并不建议你强行使用 ajax 的同步操作,很多机制并不会像我们自以为是的那样理所应当。



Atas ialah kandungan terperinci 关于同步 ajax 的 cookie问题 . Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Akan R.E.P.O. Ada Crossplay?
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk menyelesaikan ralat 403 yang dihadapi oleh permintaan jQuery AJAX Bagaimana untuk menyelesaikan ralat 403 yang dihadapi oleh permintaan jQuery AJAX Feb 20, 2024 am 10:07 AM

Tajuk: Kaedah dan contoh kod untuk menyelesaikan ralat 403 dalam permintaan AJAX jQuery Ralat 403 merujuk kepada permintaan bahawa pelayan melarang akses kepada sumber Ralat ini biasanya berlaku kerana permintaan itu tidak mempunyai kebenaran atau ditolak oleh pelayan. Apabila membuat permintaan jQueryAJAX, anda kadangkala menghadapi situasi ini Artikel ini akan memperkenalkan cara menyelesaikan masalah ini dan memberikan contoh kod. Penyelesaian: Semak kebenaran: Mula-mula pastikan alamat URL yang diminta adalah betul dan sahkan bahawa anda mempunyai kebenaran yang mencukupi untuk mengakses sumber tersebut.

Bagaimana untuk menyelesaikan ralat permintaan jQuery AJAX 403 Bagaimana untuk menyelesaikan ralat permintaan jQuery AJAX 403 Feb 19, 2024 pm 05:55 PM

jQuery ialah perpustakaan JavaScript popular yang digunakan untuk memudahkan pembangunan sisi klien. AJAX ialah teknologi yang menghantar permintaan tak segerak dan berinteraksi dengan pelayan tanpa memuatkan semula keseluruhan halaman web. Walau bagaimanapun, apabila menggunakan jQuery untuk membuat permintaan AJAX, anda kadangkala menghadapi 403 ralat. Ralat 403 biasanya ralat akses yang dinafikan pelayan, mungkin disebabkan oleh dasar keselamatan atau isu kebenaran. Dalam artikel ini, kami akan membincangkan cara menyelesaikan permintaan jQueryAJAX yang menghadapi ralat 403

Apakah yang perlu saya lakukan jika win11 tidak boleh menggunakan pelayar ie11? (win11 tidak boleh menggunakan pelayar IE) Apakah yang perlu saya lakukan jika win11 tidak boleh menggunakan pelayar ie11? (win11 tidak boleh menggunakan pelayar IE) Feb 10, 2024 am 10:30 AM

Semakin ramai pengguna mula menaik taraf sistem win11 Memandangkan setiap pengguna mempunyai tabiat penggunaan yang berbeza, ramai pengguna masih menggunakan pelayar ie11 Jadi apa yang perlu saya lakukan jika sistem win11 tidak boleh menggunakan pelayar ie. Adakah windows11 masih menyokong ie11? Mari kita lihat penyelesaiannya. Penyelesaian kepada masalah yang win11 tidak boleh menggunakan pelayar ie11 1. Pertama, klik kanan menu mula dan pilih "Command Prompt (Administrator)" untuk membukanya. 2. Selepas dibuka, terus masukkan "Netshwinsockreset" dan tekan Enter untuk mengesahkan. 3. Selepas pengesahan, masukkan "netshadvfirewallreset&rdqu

Penjelasan terperinci tentang tempat kuki penyemak imbas disimpan Penjelasan terperinci tentang tempat kuki penyemak imbas disimpan Jan 19, 2024 am 09:15 AM

Dengan populariti Internet, kami menggunakan pelayar untuk melayari Internet telah menjadi satu cara hidup. Dalam penggunaan harian penyemak imbas, kita sering menghadapi situasi di mana kita perlu memasukkan kata laluan akaun, seperti membeli-belah dalam talian, rangkaian sosial, e-mel, dsb. Maklumat ini perlu direkodkan oleh penyemak imbas supaya ia tidak perlu dimasukkan lagi pada kali berikutnya anda melawat. Ini adalah apabila kuki berguna. Apakah cookies? Kuki merujuk kepada fail data kecil yang dihantar oleh pelayan ke penyemak imbas pengguna dan disimpan secara setempat Ia mengandungi gelagat pengguna beberapa tapak web.

Bagaimana untuk menyelesaikan masalah jQuery AJAX ralat 403? Bagaimana untuk menyelesaikan masalah jQuery AJAX ralat 403? Feb 23, 2024 pm 04:27 PM

Bagaimana untuk menyelesaikan masalah jQueryAJAX ralat 403? Apabila membangunkan aplikasi web, jQuery sering digunakan untuk menghantar permintaan tak segerak. Walau bagaimanapun, kadangkala anda mungkin menghadapi kod ralat 403 apabila menggunakan jQueryAJAX, menunjukkan bahawa akses dilarang oleh pelayan. Ini biasanya disebabkan oleh tetapan keselamatan bahagian pelayan, tetapi terdapat cara untuk mengatasinya. Artikel ini akan memperkenalkan cara menyelesaikan masalah ralat jQueryAJAX 403 dan memberikan contoh kod khusus. 1. membuat

PHP dan Ajax: Membina enjin cadangan autolengkap PHP dan Ajax: Membina enjin cadangan autolengkap Jun 02, 2024 pm 08:39 PM

Bina enjin cadangan autolengkap menggunakan PHP dan Ajax: Skrip sisi pelayan: mengendalikan permintaan Ajax dan mengembalikan cadangan (autocomplete.php). Skrip pelanggan: Hantar permintaan Ajax dan cadangan paparan (autocomplete.js). Kes praktikal: Sertakan skrip dalam halaman HTML dan nyatakan pengecam elemen input carian.

Bagaimana untuk mendapatkan pembolehubah daripada kaedah PHP menggunakan Ajax? Bagaimana untuk mendapatkan pembolehubah daripada kaedah PHP menggunakan Ajax? Mar 09, 2024 pm 05:36 PM

Menggunakan Ajax untuk mendapatkan pembolehubah daripada kaedah PHP adalah senario biasa dalam pembangunan web Melalui Ajax, halaman boleh diperoleh secara dinamik tanpa menyegarkan data. Dalam artikel ini, kami akan memperkenalkan cara menggunakan Ajax untuk mendapatkan pembolehubah daripada kaedah PHP, dan memberikan contoh kod khusus. Pertama, kita perlu menulis fail PHP untuk mengendalikan permintaan Ajax dan mengembalikan pembolehubah yang diperlukan. Berikut ialah kod contoh untuk fail PHP mudah getData.php:

Bagaimana untuk membatalkan lompat automatik ke Edge apabila membuka IE dalam Win10_Solution ke lompat automatik halaman pelayar IE Bagaimana untuk membatalkan lompat automatik ke Edge apabila membuka IE dalam Win10_Solution ke lompat automatik halaman pelayar IE Mar 20, 2024 pm 09:21 PM

Baru-baru ini, ramai pengguna win10 mendapati bahawa pelayar IE mereka sentiasa melompat ke pelayar tepi secara automatik apabila menggunakan pelayar komputer Jadi bagaimana untuk mematikan lompat ke tepi automatik apabila membuka IE dalam win10? Biarkan tapak ini dengan teliti memperkenalkan kepada pengguna cara melompat ke tepi dan menutup secara automatik apabila membuka IE dalam win10. 1. Kami log masuk ke pelayar tepi, klik... di penjuru kanan sebelah atas, dan cari pilihan tetapan lungsur turun. 2. Selepas kami memasukkan tetapan, klik Penyemak Imbas Lalai di lajur kiri. 3. Akhir sekali, dalam keserasian, kami menandakan kotak untuk tidak membenarkan tapak web dimuat semula dalam mod IE dan mulakan semula pelayar IE.

See all articles