首先背景就是測試同學發現我們的網頁在ie9中展示不正確,實際是所有非同步的介面都沒有執行。然後我就開始了苦逼的排查過程。我們所有非同步介面都是使用jQuery的ajax方法發出的,使用的jquery版本是1.11.0。 我最先定位到的是ajax方法返回status=0,statusText=No Tra ...
首先背景就是測試同學發現我們的網頁在ie9中展示不正確,實際是所有非同步的介面都沒有執行。然後我就開始了苦逼的排查過程。我們所有非同步介面都是使用jQuery的ajax方法發出的,使用的jquery版本是1.11.0。
我最先定位到的是ajax方法返回status=0,statusText=No Transport。然後開始了我的查找問題之旅,我在網上所查出的資料都說這個是由於跨域造成的,加上$.support.cros=true
就可以。但實際我們項目訪問的介面並不屬於跨域。所以雖然加上這個屬性可以解決問題,但並沒有找到根源。
於是我開始看jquery的源碼,先定位了發生錯誤的代碼段,先貼上報錯點:
1 // Get transport 2 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); 3 4 // If no transport, we auto-abort 5 if ( !transport ) { 6 done( -1, "No Transport" ); 7 } else { 8 jqXHR.readyState = 1; 9 10 // Send global event 11 if ( fireGlobals ) { 12 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); 13 } 14 // Timeout 15 if ( s.async && s.timeout > 0 ) { 16 timeoutTimer = setTimeout(function() { 17 jqXHR.abort("timeout"); 18 }, s.timeout ); 19 } 20 21 try { 22 state = 1; 23 transport.send( requestHeaders, done ); 24 } catch ( e ) { 25 // Propagate exception as error if not done 26 if ( state < 2 ) { 27 done( -1, e ); 28 // Simply rethrow otherwise 29 } else { 30 throw e; 31 } 32 } 33 }
此處transport =false。剛開始並不能完全看懂jquery源碼,於是請教了我們公司的一位大神,在大神的指導下我開始排查為何transport =false。經過萬能的斷點調試,發現是由於下列代碼中的options.crossDomain = true
導致的不能進入此段正確發送請求的關鍵代碼(具體的代碼作用不作分析)。到此終於定位罪魁禍首是最初我為了在html頁面可以直接訪問api伺服器而增加的crossDomain=true
的設置(因為html和api伺服器屬於不同功能變數名稱)。(此處並未結束,請繼續往下看)
1 // Determine support properties 2 support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); 3 xhrSupported = support.ajax = !!xhrSupported; 4 5 // Create transport if the browser can provide an xhr 6 if ( xhrSupported ) { 7 8 jQuery.ajaxTransport(function( options ) { 9 // Cross domain only allowed if supported through XMLHttpRequest 10 if ( !options.crossDomain || support.cors ) { 11 12 var callback; 13 14 return { 15 send: function( headers, complete ) { 16 var i, 17 xhr = options.xhr(), 18 id = ++xhrId; 19 20 // Open the socket 21 xhr.open( options.type, options.url, options.async, options.username, options.password ); 22 23 // Apply custom fields if provided 24 if ( options.xhrFields ) { 25 for ( i in options.xhrFields ) { 26 xhr[ i ] = options.xhrFields[ i ]; 27 } 28 } 29 30 // Override mime type if needed 31 if ( options.mimeType && xhr.overrideMimeType ) { 32 xhr.overrideMimeType( options.mimeType ); 33 } 34 35 // X-Requested-With header 36 // For cross-domain requests, seeing as conditions for a preflight are 37 // akin to a jigsaw puzzle, we simply never set it to be sure. 38 // (it can always be set on a per-request basis or even using ajaxSetup) 39 // For same-domain requests, won't change header if already provided. 40 if ( !options.crossDomain && !headers["X-Requested-With"] ) { 41 headers["X-Requested-With"] = "XMLHttpRequest"; 42 } 43 44 // Set headers 45 for ( i in headers ) { 46 // Support: IE<9 47 // IE's ActiveXObject throws a 'Type Mismatch' exception when setting 48 // request header to a null-value. 49 // 50 // To keep consistent with other XHR implementations, cast the value 51 // to string and ignore `undefined`. 52 if ( headers[ i ] !== undefined ) { 53 xhr.setRequestHeader( i, headers[ i ] + "" ); 54 } 55 } 56 57 // Do send the request 58 // This may raise an exception which is actually 59 // handled in jQuery.ajax (so no try/catch here) 60 xhr.send( ( options.hasContent && options.data ) || null ); 61 62 // Listener 63 callback = function( _, isAbort ) { 64 var status, statusText, responses; 65 66 // Was never called and is aborted or complete 67 if ( callback && ( isAbort || xhr.readyState === 4 ) ) { 68 // Clean up 69 delete xhrCallbacks[ id ]; 70 callback = undefined; 71 xhr.onreadystatechange = jQuery.noop; 72 73 // Abort manually if needed 74 if ( isAbort ) { 75 if ( xhr.readyState !== 4 ) { 76 xhr.abort(); 77 } 78 } else { 79 responses = {}; 80 status = xhr.status; 81 82 // Support: IE<10 83 // Accessing binary-data responseText throws an exception 84 // (#11426) 85 if ( typeof xhr.responseText === "string" ) { 86 responses.text = xhr.responseText; 87 } 88 89 // Firefox throws an exception when accessing 90 // statusText for faulty cross-domain requests 91 try { 92 statusText = xhr.statusText; 93 } catch( e ) { 94 // We normalize with Webkit giving an empty statusText 95 statusText = ""; 96 } 97 98 // Filter status for non standard behaviors 99 100 // If the request is local and we have data: assume a success 101 // (success with no data won't get notified, that's the best we 102 // can do given current implementations) 103 if ( !status && options.isLocal && !options.crossDomain ) { 104 status = responses.text ? 200 : 404; 105 // IE - #1450: sometimes returns 1223 when it should be 204 106 } else if ( status === 1223 ) { 107 status = 204; 108 } 109 } 110 } 111 112 // Call complete if needed 113 if ( responses ) { 114 complete( status, statusText, responses, xhr.getAllResponseHeaders() ); 115 } 116 }; 117 118 if ( !options.async ) { 119 // if we're in sync mode we fire the callback 120 callback(); 121 } else if ( xhr.readyState === 4 ) { 122 // (IE6 & IE7) if it's in cache and has been 123 // retrieved directly we need to fire the callback 124 setTimeout( callback ); 125 } else { 126 // Add to the list of active xhr callbacks 127 xhr.onreadystatechange = xhrCallbacks[ id ] = callback; 128 } 129 }, 130 131 abort: function() { 132 if ( callback ) { 133 callback( undefined, true ); 134 } 135 } 136 }; 137 } 138 }); 139 }
到此本來應該結束了,但並沒有,雖然我增加了crossDomain=true
的設置,但為何其他瀏覽器可以正常訪問,而ie9不行呢。我繼續調試代碼,最終發現其他瀏覽器上述代碼中support.cors = true
,而ie9下這個屬性等於false。從上述代碼中也可以看到,這個屬性的判斷來自於support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported )
,其中xhrSupported= new window.XMLHttpRequest()
,ie9中XMLHttpRequest沒有withCredentials屬性。也就是說這個問題是由於我的亂用屬性加上各瀏覽器相容性問題而導致的。