Ajax 동기화의 쿠키 문제에 대하여
머리말
이런 문제가 발생하면 정말 속수무책입니다. 프론트엔드 브라우저 호환성은 언제나 골치 아픈 문제였습니다.
이 글은 그토록 당황스럽고 무기력했던 날만을 기록합니다. 모두의 심심함을 달래는데 활용해보세요 T_T
현장이 재현되어있습니다
동료: 어서! 어서 해봐요! 온라인에서 문제가 발생했습니다. !
나: 대체 뭐야?! 뭐야?!
동료: 이번 출시로 인한 건가요?
나: 롤백하세요! 롤백! (먹으려고 할 때 왜 체인을 떨어뜨리는가! 배를 돌볼 수 없잖아! 빨리 확인)
...
혼란스러운 대화 끝에 진정할 수 있는 건 '지뢰 제거' 뿐이다. ".
롤백, 프록시, 패킷 캡처, 비교, 단일 요소 문제 해결. . .
콤보 펀치 세트를 마친 후 마침내 결함을 찾는 데 향 한 개 정도가 걸렸습니다. Ajax 동기화 콜백에 문제가 있는 것으로 밝혀졌습니다! 무모한! 그러면 안 됩니다! 그런 수술이 있나요? !
문제 재발
한 문장으로 문제 요약
ajax를 사용하여 "동기화" 요청을 하면
성공
에서 이 대상 쿠키를 반환합니다. > 콜백이 실패했습니다!document.cookie
는 ajax 실행이 완료될 때까지 업데이트되지 않습니다success
回调中读取此目标cookie 失败!ajax执行结束后document.cookie
才会被更新
影响范围
PC 端和 Android 端影响范围小,属于偶现。
IOS 端是重灾区,出来 Chrome 和 Safari 浏览器外的绝大多说浏览器都会出现此问题,并且 App 内置的 Webview 环境同样不能幸免。
在本同步请求回调内预读取本请求返回的 cookie 会产生问题。
半壁江山都沦陷了,我要这铁棒有何用!
追因溯果
小范围的兼容问题我姑且可以饶你,奈何你如此猖狂,怎能任你瞒天过海!
纵向对比
排除一些干扰项,还原其本质,我们分别用框架nej
,jQuery
和js
写几个相同功能的“同步” 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([ '{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】使用 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】自己实现的 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: '/api', async: false, type: 'POST', success: function (result) { alert("cookie:\n" + document.cookie) } }); </script> </body> </html>
三个文件都是一样的,在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('/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);
好了,万事大吉,run 一把
$ node serve.js
操作
我们依次执行如下操作,
使用 ios 端 QQ 浏览器,清空所有缓存
加载其中一个页面,观察是否有目标 cookie 输出
执行刷新操作,观察是否有目标 cookie 输出,比较 cookie 输出的时间戳,确认是否为上次 cookie 的同步结果而非本次请求获取的 cookie,
清空所有缓存,切换目标 html 文件,循环执行2,3,4步骤
结果
【nej.html】
纯净环境加载,未读取到目标 cookie
刷新加载,读取到上一次请求返回的 cookie
【jquery.html】
纯净环境加载,未读取到目标 cookie
刷新加载,未读取到目标 cookie
【js.html】
纯净环境加载,未读取到目标 cookie
刷新加载,未读取到目标 cookie
咦?结果不一样!使用 nej 的第二次加载读取到了第一次 cookie。其他的两次均为获取到。
原因
nej 依赖框架的加载是异步的,当同步请求发起时,dom 已经加载完毕,回调相应时,document.cookie
已经呈“ready”状态,可读可写。但请求依然获取不到自身返回携带的 cookie。
而其他两种加载的机制阻塞了 dom 的加载,导致同步请求发起时,dom 尚未加载完成,回调相应时,document.cookie
영향 범위
PC와 Android에 미치는 영향 범위는 작으며 간헐적으로 발생합니다.
IOS가 가장 큰 타격을 받는 분야입니다. Chrome과 Safari를 제외한 대부분의 브라우저에서 이 문제가 발생하며, 앱에 내장된 Webview 환경도 면역되지 않습니다.
이 동기 요청 콜백 내에서 이 요청에 의해 반환된 쿠키를 미리 읽으면 문제가 발생할 수 있습니다.
국토의 절반이 망했는데 이 쇠막대가 무슨 소용이 있겠는가!- 원인 추적
- 미세한 호환성 문제는 용서할 수 있는데 너무 오만해서 어떻게 숨길 수 있겠습니까?
수직 비교
일부 간섭 항목을 제거하고 본질을 복원하기 위해
nej
, jQuery
및 js
프레임워크를 사용하여 여러 항목을 작성합니다. 동일 "동기화" 기능의 데모입니다. 기다려 보겠습니다. . 【nej.html】NEJ 라이브러리 사용
$('document').click(function () { // TODO 发起同步请求 });
- 【jquery.html】jQuery 라이브러리 사용
_$ajax({ url: '/api', async: false, type: 'POST', success: function (result) { setTimeout(function(){ // do something 在此处获取 cookie 操作是安全的 },0) } });
로그인 후 복사로그인 후 복사【js.html】직접 구현한 ajax 요청 기능
rrreee 세 파일은 동일하며 html로 로딩 후 그런 다음 콜백에 다음은 노드를 사용하여 쓰기 가능한 쿠키 서비스를 구현합니다.
【serve.js】 rrreee
document.cookie
를 인쇄하여 쿠키를 반환하는 동기 요청을 시작하여 콜백 중에 쿠키가 기록되었는지 확인합니다. rrreee
Operation- 다음 작업을 순서대로 수행합니다.
- 🎜ios에서 QQ 브라우저 사용 , 모든 캐시 지우기🎜🎜🎜🎜페이지 중 하나를 로드하고 대상 쿠키 출력이 있는지 관찰🎜🎜🎜🎜새로 고침 작업을 수행하고, 대상 쿠키 출력이 있는지 관찰하고, 쿠키 출력의 타임스탬프를 비교하고 확인합니다. 마지막 쿠키의 동기화 결과인지 여부 이 요청으로 얻은 쿠키 대신 🎜🎜🎜🎜모든 캐시를 지우고 대상 html 파일을 전환하고 2, 3, 4단계를 반복🎜🎜
document.cookie
가 이미 "준비" 상태입니다. 상태를 읽고 쓸 수 있습니다. 그러나 요청은 여전히 자체적으로 반환된 쿠키를 얻을 수 없습니다. 🎜🎜🎜 🎜다른 두 가지 로드 메커니즘은 DOM 로드를 차단하여 동기 요청이 시작될 때 DOM이 로드되지 않게 만듭니다. 콜백이 응답하면
document.cookie
는 여전히 쓸 수 없습니다. 🎜🎜Single Factor Control🎜🎜위 HTML 파일의 로직을 수정하겠습니다. 🎜문서를 클릭하고 트리거될 때까지 동기화 요청을 지연하세요. 🎜다음🎜rrreee🎜은 여전히 위의 실행 단계입니다. 결과를 살펴보겠습니다🎜🎜Results🎜🎜[nej.html]🎜🎜🎜🎜순수 환경 로딩, 대상 쿠키를 읽지 못했습니다🎜🎜🎜🎜새로 고침 로딩 , 이전 요청에서 반환된 쿠키 읽기🎜🎜🎜🎜[jquery.html]🎜🎜🎜🎜순수한 환경 로딩, 대상 쿠키가 읽히지 않음🎜🎜🎜🎜로드를 새로 고치고 이전 요청에서 반환된 쿠키 읽기🎜🎜 🎜🎜【js.html】🎜🎜🎜🎜순수 환경 로딩, 대상 쿠키를 읽지 않음🎜 刷新加载,读取到上一次请求返回的 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浏览器环境下,失败率极高。
解决方案
只有问题没有方案的都是在耍流氓!
方案1 - 明修栈道暗度陈仓
将回调方法中的 cookie 获取方法转化为异步操作。
_$ajax({ url: '/api', async: false, type: 'POST', success: function (result) { setTimeout(function(){ // do something 在此处获取 cookie 操作是安全的 },0) } });
方案2 - 不抵抗政策
没有把握的方案,我们是要斟酌着实施的。
如果你不能100%却被操作的安全性,那并不建议你强行使用 ajax 的同步操作,很多机制并不会像我们自以为是的那样理所应当。
위 내용은 Ajax 동기화의 쿠키 문제에 대하여의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











제목: jQuery AJAX 요청의 403 오류를 해결하는 방법 및 코드 예제 403 오류는 서버가 리소스에 대한 액세스를 금지하는 요청을 의미합니다. 이 오류는 일반적으로 요청에 권한이 없거나 서버에서 거부되기 때문에 발생합니다. jQueryAJAX 요청을 할 때 가끔 이런 상황이 발생합니다. 이 기사에서는 이 문제를 해결하는 방법을 소개하고 코드 예제를 제공합니다. 해결 방법: 권한 확인: 먼저 요청한 URL 주소가 올바른지 확인하고 리소스에 액세스할 수 있는 충분한 권한이 있는지 확인하십시오.

jQuery는 클라이언트 측 개발을 단순화하는 데 사용되는 인기 있는 JavaScript 라이브러리입니다. AJAX는 전체 웹 페이지를 다시 로드하지 않고 비동기 요청을 보내고 서버와 상호 작용하는 기술입니다. 그러나 jQuery를 사용하여 AJAX 요청을 할 때 가끔 403 오류가 발생합니다. 403 오류는 일반적으로 보안 정책이나 권한 문제로 인해 서버 거부 액세스 오류입니다. 이 기사에서는 403 오류가 발생한 jQueryAJAX 요청을 해결하는 방법에 대해 설명합니다.

점점 더 많은 사용자들이 win11 시스템을 업그레이드하기 시작하고 있습니다. 사용자마다 사용 습관이 다르기 때문에 여전히 많은 사용자들이 ie11 브라우저를 사용하고 있습니다. 그렇다면 win11 시스템에서 ie 브라우저를 사용할 수 없으면 어떻게 해야 합니까? windows11은 여전히 ie11을 지원하나요? 해결책을 살펴보겠습니다. win11에서 ie11 브라우저를 사용할 수 없는 문제 해결 방법 1. 먼저 시작 메뉴를 마우스 오른쪽 버튼으로 클릭한 후 "명령 프롬프트(관리자)"를 선택하여 엽니다. 2. 연 후 "Netshwinsockreset"을 직접 입력하고 Enter를 눌러 확인합니다. 3. 확인 후 "netshadvfirewallreset&rdqu"를 입력하세요.

인터넷의 대중화로 인해 우리는 브라우저를 사용하여 인터넷 서핑을 하는 것이 생활 방식이 되었습니다. 브라우저를 일상적으로 사용하다 보면 온라인 쇼핑, 소셜 네트워킹, 이메일 등 계정 비밀번호를 입력해야 하는 상황에 자주 직면하게 됩니다. 이 정보는 다음에 방문할 때 다시 입력할 필요가 없도록 브라우저에 기록되어야 합니다. 이때 쿠키가 유용합니다. 쿠키란 무엇입니까? 쿠키는 서버가 사용자의 브라우저에 전송하고 로컬에 저장되는 작은 데이터 파일을 말하며 일부 웹사이트의 사용자 행동을 포함합니다.

Ajax를 사용하여 PHP 메소드에서 변수를 얻는 것은 웹 개발의 일반적인 시나리오입니다. Ajax를 통해 데이터를 새로 고치지 않고도 페이지를 동적으로 얻을 수 있습니다. 이 기사에서는 Ajax를 사용하여 PHP 메소드에서 변수를 가져오는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 먼저 Ajax 요청을 처리하고 필요한 변수를 반환하기 위해 PHP 파일을 작성해야 합니다. 다음은 간단한 PHP 파일 getData.php에 대한 샘플 코드입니다.

jQueryAJAX 오류 403 문제를 해결하는 방법은 무엇입니까? 웹 애플리케이션을 개발할 때 jQuery는 종종 비동기 요청을 보내는 데 사용됩니다. 그러나 때때로 jQueryAJAX를 사용할 때 서버에서 액세스가 금지되었음을 나타내는 오류 코드 403이 발생할 수 있습니다. 이는 일반적으로 서버 측 보안 설정으로 인해 발생하지만 문제를 해결하는 방법이 있습니다. 이 기사에서는 jQueryAJAX 오류 403 문제를 해결하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. 만들다

PHP 및 Ajax를 사용하여 자동 완성 제안 엔진 구축: 서버측 스크립트: Ajax 요청을 처리하고 제안을 반환합니다(autocomplete.php). 클라이언트 스크립트: Ajax 요청을 보내고 제안을 표시합니다(autocomplete.js). 실제 사례: HTML 페이지에 스크립트를 포함하고 검색 입력 요소 식별자를 지정합니다.

쿠키 설정에 대한 일반적인 문제와 해결 방법, 구체적인 코드 예제가 필요합니다. 인터넷의 발전과 함께 쿠키는 가장 일반적인 기존 기술 중 하나로 웹사이트와 애플리케이션에서 널리 사용되었습니다. 간단히 말해서 쿠키는 로그인 이름, 장바구니 내용, 웹사이트 기본 설정 등을 포함하여 웹사이트에 사용자의 정보를 저장하는 데 사용할 수 있는 사용자의 컴퓨터에 저장되는 데이터 파일입니다. 쿠키는 개발자에게 필수적인 도구이지만 동시에 쿠키 설정도 자주 접하게 됩니다.
