Heim > Artikel > Web-Frontend > Beheben Sie den Fehler, der entsteht, wenn jQuery JSONP_jquery verwendet
Was ist eine Domain?
Unter domänenübergreifend versteht man den Zugriff auf Daten in einer Domäne in einer anderen Domäne. Wenn Sie einfach den Inhalt einer anderen Domain laden, ohne auf die Daten zuzugreifen, ist die domänenübergreifende Nutzung sehr einfach, beispielsweise durch die Verwendung von iframe. Wenn Sie diese Daten jedoch von einer anderen Domäne laden und verwenden müssen, ist dies problematischer. Aus Sicherheitsgründen unterliegen Browser in dieser Situation strengen Einschränkungen und es müssen sowohl auf dem Client als auch auf dem Server einige Einstellungen vorgenommen werden, um domänenübergreifende Anforderungen umzusetzen.
Einführung in JSONP
JSONP (JSON mit Padding) ist eine häufig verwendete domänenübergreifende Methode, unterstützt jedoch nur JS-Skripte und Daten im JSON-Format. Wie der Name schon sagt, ist
JSONP ein technisches Mittel, das JSON als Shim verwendet, um domänenübergreifende Anfragen umzusetzen. Das Grundprinzip besteht darin, die Tatsache zu nutzen, dass die 3f1c4e4b6b16bbbd69b2ee476dc4f83a-Tags von Natur aus domänenübergreifend sind, und sie nach dem Laden zum Laden von JSON-Daten aus einer anderen Domäne zu verwenden Wenn der Vorgang abgeschlossen ist, wird automatisch ein Rückruf ausgeführt. Die Funktion benachrichtigt den Anrufer. Dieser Prozess erfordert Serverunterstützung von einer anderen Domäne, sodass eine domänenübergreifende Implementierung auf diese Weise nicht willkürlich ist.
JQuerys Unterstützung für JSONP
Das Ajax-Objekt von JQuery unterstützt domänenübergreifende Anforderungen im JSONP-Modus, indem der Parameter crossDomain als true und der Parameter 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() { // 请求完成时的处理函数 });müssen Sie jsoncallback=? im Parameter angeben. Dies ist die zuvor erwähnte Callback-Funktion, die automatisch einen zufällig generierten Wert (Callback-Funktionsname) verwendet ), um den Fragezeichenteil im Parameter zu ersetzen und so einen Parameter in der Form
jsoncallback=jQueryxxxxxxx zu bilden, und verwenden Sie dann die GET-Methode zusammen mit anderen Parametern, um eine Anfrage zu stellen. Wenn Sie die erste Methode verwenden, fügt JQuery automatisch den Parameter jsoncallback nach der Anforderungsadresse hinzu, solange der Wert des Parameters dataType als jsonp angegeben ist Sie müssen es nicht manuell hinzufügen.
Fehler bei domänenübergreifenden JQuery-Anfragen: Fehlerbehandlung Domänenübergreifende Anfragen können fehlschlagen. Beispielsweise verweigern die Sicherheitseinstellungen des Servers der anderen Partei die Annahme von Anfragen von uns (wir stehen nicht auf der Vertrauensliste der anderen Partei), oder das Netzwerk ist nicht verfügbar oder der Server der anderen Partei geschlossen ist oder die Anforderungsadresse oder die Parameter falsch sind, was dazu führt, dass der Server Fehler meldet usw.
Wenn in JQuery eine Anfrage mit Ajax oder getJSON gesendet wird, wird ein jqXHR-Objekt [3] zurückgegeben. Dieses Objekt implementiert das Promise-Protokoll, sodass wir dessen Done-, Fail-, Always- und andere Schnittstellen zur Verarbeitung von Rückrufen verwenden können. Wir können es beispielsweise in seinem Fail-Callback verwenden, um Fehler zu behandeln, wenn die Anfrage fehlschlägt:
这种方式能够处理“正常的错误”,例如超时、请求被中止、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]; } }; }
Der obige Code wurde unter IE8, IE11, Chrome, Firefox, Opera und 360 getestet. 360 ist die IE-Kernelversion und andere Browser wurden noch nicht getestet.
Ich hoffe, dieser Artikel hilft Ihnen beim Lernen und hilft Ihnen, die Fehler zu beheben, die auftreten, wenn jQuery JSONP verwendet.