Maison > Article > interface Web > Résoudre l'erreur générée lorsque jQuery utilise JSONP_jquery
Qu'est-ce qu'un domaine
Interdomaine signifie accéder aux données d’un domaine dans un autre domaine. Si vous chargez simplement le contenu d'un autre domaine sans accéder aux données, le cross-domain est très simple, comme utiliser iframe. Mais si vous devez charger et utiliser ces données depuis un autre domaine, ce sera plus gênant. Pour des raisons de sécurité, les navigateurs ont des restrictions strictes sur cette situation, et certains paramètres doivent être définis à la fois sur le client et sur le serveur pour implémenter les requêtes inter-domaines.
Introduction à JSONP
JSONP (JSON with Padding) est une méthode inter-domaines couramment utilisée, mais elle ne prend en charge que les scripts JS et les données au format JSON. Comme son nom l'indique,
JSONP est un moyen technique qui utilise JSON comme cale pour implémenter des requêtes inter-domaines. Le principe de base est de profiter du fait que les balises HTML 3f1c4e4b6b16bbbd69b2ee476dc4f83a sont intrinsèquement inter-domaines et de les utiliser pour charger des données JSON à partir d'un autre domaine après le chargement. terminé, un rappel sera automatiquement exécuté. La fonction informe l'appelant. Ce processus nécessite la prise en charge du serveur d'un autre domaine, donc une telle implémentation inter-domaines n'est pas arbitraire.
Prise en charge de JQuery pour JSONP
L'objet Ajax de JQuery prend en charge les requêtes inter-domaines en mode JSONP en spécifiant le paramètre crossDomain comme true et le paramètre dataType
// 设置crossDomain和dataType参数以使用JSONP $.ajax({ dataType: "jsonp", url: "http://www.example.com/xxx", crossDomain: true, data: { } }).done(function() { // 请求完成时的处理函数 }); // 使用getJSON $.getJSON("http://www.example.com/xxx?jsoncallback=?", { // 参数 }, function() { // 请求完成时的处理函数 });, vous devez spécifier jsoncallback=? dans le paramètre. Il s'agit de la fonction de rappel mentionnée précédemment qui utilisera automatiquement une valeur générée aléatoirement (nom de la fonction de rappel). ) pour remplacer la partie point d'interrogation dans le paramètre, formant ainsi un paramètre sous la forme de
jsoncallback=jQueryxxxxxxx, puis utilisez la méthode GET avec d'autres paramètres pour faire une requête. Lors de l'utilisation de la première méthode, tant que la valeur du paramètre dataType est spécifiée comme jsonp, JQuery ajoutera automatiquement le paramètre jsoncallback après l'adresse de la requête, il y a donc pas besoin de l'ajouter manuellement.
Défauts de requêtes inter-domaines JQuery : gestion des erreurs Les requêtes inter-domaines peuvent échouer. Par exemple, les paramètres de sécurité du serveur de l'autre partie refusent d'accepter les demandes de notre part (nous ne sommes pas dans la liste de confiance de l'autre partie), ou le réseau est indisponible, ou le serveur de l'autre partie. est fermé, ou l'adresse ou les paramètres de la demande sont incorrects, ce qui amène le serveur à signaler des erreurs, etc.
Dans JQuery, lorsqu'une requête est envoyée en utilisant ajax ou getJSON, un objet jqXHR [3] sera renvoyé. Cet objet implémente le protocole Promise, nous pouvons donc utiliser ses interfaces done, fail, always et autres pour gérer les rappels. Par exemple, nous pouvons l'utiliser dans son rappel d'échec pour gérer les erreurs lorsque la requête échoue :
这种方式能够处理“正常的错误”,例如超时、请求被中止、JSON解析出错等等。但它对那些“非正常的错误”,例如网络不通、服务器已关闭等情况的支持并不好。
例如当对方服务器无法正常访问时,在Chrome下你会在控制台看到一条错误信息:
JQuery不会处理该错误,而是选择“静静地失败”:fail回调不会执行,你的代码也不会得到任何反馈,所以你没有处理这种错误的机会,也无法向用户报告错误。
一个例外是在IE8。在IE8中,当网络无法访问时,3f1c4e4b6b16bbbd69b2ee476dc4f83a标签一样会返回加载成功的信息,所以JQuery无法根据3f1c4e4b6b16bbbd69b2ee476dc4f83a标签的状态来判断是否已成功加载,但它发现3f1c4e4b6b16bbbd69b2ee476dc4f83a标签“加载成功”后回调函数却没有执行,所以JQuery以此判断这是一个“解析错误”(回调代码没有执行,很可能是返回的数据不对导致没有执行或执行失败),因此返回的错误信息将是“xxxx was not called”,其中的xxxx为回调函数的名称。
也就是说,由于IE8(IE7也一样)的这种奇葩特性,导致在发生网络不通等“非正常错误”时,JQuery反而无法选择“静默失败”策略,于是我们可以由此受益,得到了处理错误的机会。例如在这种情况下,上面的例子将会弹出“xxxx was not called”的对话框。
解决方案
当遇到“非正常错误”时,除了IE7、8以外,JQuery的JSONP在较新的浏览器中全部会“静默失败”。但很多时候我们希望能够捕获和处理这种错误。
实际上在这些浏览器中,3f1c4e4b6b16bbbd69b2ee476dc4f83a标签在遇到这些错误时会触发error事件。例如如果是我们自己来实现JSONP的话可以这样:
var ele = document.createElement('script'); ele.type = "text/javascript"; ele.src = '...'; ele.onerror = function() { alert('error'); }; ele.onload = function() { alert('load'); }; document.body.appendChild(ele);
在新浏览器中,当发生错误时将会触发error事件,从而执行onerror回调弹出alert对话框:
但是麻烦在于,JQuery不会把这个3f1c4e4b6b16bbbd69b2ee476dc4f83a标签暴露给我们,所以我们没有机会为其添加onerror事件处理器。
下面是JQuery实现JSONP的主要代码:
jQuery.ajaxTransport( "script", function(s) { if ( s.crossDomain ) { var script, head = document.head || jQuery("head")[0] || document.documentElement; return { send: function( _, callback ) { script = document.createElement("script"); script.async = true; ... script.src = s.url; script.onload = script.onreadystatechange = ...; head.insertBefore( script, head.firstChild ); }, abort: function() { ... } }; } });
可以看到script是一个局部变量,从外部无法获取到。
那有没有解决办法呢?当然有:
前两种不说了,如果愿意大可以选择。下面介绍另一种技巧。
通过以上源码可以发现,JQuery虽然没有暴露出script变量,但是它却“暴露”出了3f1c4e4b6b16bbbd69b2ee476dc4f83a标签的位置。通过send方法的最后一句:
head.insertBefore( script, head.firstChild );
可以知道这个动态创建的新创建标签被添加为head的第一个元素。而我们反其道而行之,只要能获得这个head元素,不就可以获得这个script了吗?head是什么呢?继续看源码,看head是怎么来的:
head = document.head || jQuery("head")[0] || document.documentElement;
原来如此,我们也用同样的方法获取就可以了,所以补全前面的那个例子,如下:
var xhr = $.getJSON(...); // for "normal error" and ie 7, 8 xhr.fail(function(jqXHR, textStatus, ex) { alert('request failed, cause: ' + ex.message); }); // for 'abnormal error' in other browsers var head = document.head || $('head')[0] || document.documentElement; // code from jquery var script = $(head).find('script')[0]; script.onerror(function(evt) { alert('error'); });
这样我们就可以在所有浏览器(严格来说是绝大部分,因为我没有测试全部浏览器)里捕获到“非正常错误”了。
这样捕获错误还有一个好处:在IE7、8之外的其他浏览器中,当发生网络不通等问题时,JQuery除了会静默失败,它还会留下一堆垃圾不去清理,即新创建的3f1c4e4b6b16bbbd69b2ee476dc4f83a标签和全局回调函数。虽然留在那也没什么大的危害,但如果能够顺手将其清理掉不是更好吗?所以我们可以这样实现onerror:
// handle error alert('error'); // do some clean // delete script node if (script.parentNode) { script.parentNode.removeChild(script); } // delete jsonCallback global function var src = script.src || ''; var idx = src.indexOf('jsoncallback='); if (idx != -1) { var idx2 = src.indexOf('&'); if (idx2 == -1) { idx2 = src.length; } var jsonCallback = src.substring(idx + 13, idx2); delete window[jsonCallback]; }
这样一来就趋于完美了。
完整代码
function jsonp(url, data, callback) { var xhr = $.getJSON(url + '?jsoncallback=?', data, callback); // request failed xhr.fail(function(jqXHR, textStatus, ex) { /* * in ie 8, if service is down (or network occurs an error), the arguments will be: * * testStatus: 'parsererror' * ex.description: 'xxxx was not called' (xxxx is the name of jsoncallback function) * ex.message: (same as ex.description) * ex.name: 'Error' */ alert('failed'); }); // ie 8+, chrome and some other browsers var head = document.head || $('head')[0] || document.documentElement; // code from jquery var script = $(head).find('script')[0]; script.onerror = function(evt) { alert('error'); // do some clean // delete script node if (script.parentNode) { script.parentNode.removeChild(script); } // delete jsonCallback global function var src = script.src || ''; var idx = src.indexOf('jsoncallback='); if (idx != -1) { var idx2 = src.indexOf('&'); if (idx2 == -1) { idx2 = src.length; } var jsonCallback = src.substring(idx + 13, idx2); delete window[jsonCallback]; } }; }
Le code ci-dessus a été testé sous IE8, IE11, Chrome, FireFox, Opera et 360. 360 est la version du noyau IE et les autres navigateurs n'ont pas encore été testés.
J'espère que cet article vous aidera à apprendre et à résoudre les erreurs qui se produisent lorsque jQuery utilise JSONP.