2012-04-24 46 views
1

我正在嘗試基於SproutCore 1.8構建一個Web應用程序。爲了從服務器檢索數據,應用程序將CORS請求發送到在單獨的域上運行的REST Web服務。在IE9中SproutCore的異源請求

該代碼,如下所示:

var request = SC.Request.getUrl('http://example.com/some/path'); 
request.set('attachIdentifyingHeaders', NO);  
request.json().notify(this, this.didFetchData).send(); 

這工作在Chrome,Safari和Firefox偉大的,但它不會在Internet Explorer 9的工作事實上,IE9運行到一個JavaScript錯誤SproutCore內部請求實現中的「拒絕訪問」。錯誤引發在這行:

// initiate request. 
rawRequest.open(this.get('type'), this.get('address'), async); 

經過一番短暫的調查,我發現,微軟實現了在IE9 CORS請求的專用XDomainRequest對象。這似乎並不受SproutCore的支持,我從這些線(SproutCore的本地請求執行選擇)推斷:

return tryThese(
    function() { return new XMLHttpRequest(); }, 
    function() { return new ActiveXObject('Msxml2.XMLHTTP'); }, 
    function() { return new ActiveXObject('Microsoft.XMLHTTP'); } 
); 

這是SproutCore的的缺點還是我失去了一些東西?如果是這樣,你有沒有建議我如何解決這個問題而無需編寫我自己的請求抽象?

請注意,CORS是我正在使用的現有服務器基礎結構的一項要求。我既不能將服務放在與提供客戶端的服務器相同的域中,也不能使用反向代理或類似的基礎設施來解決問題。

回答

2

你有兩個選擇

1)哈克Sproutcore本身。你可以嘗試添加

function() {return new XDomainRequest(); }

到差異XHR運輸工具該列表。首先添加它;如果它在瀏覽器中可用,則將使用它,否則代碼將退回到其他對象。 2)如果你不想修改SC來源,你可以創建自己的App.CorsResponse類,擴展SC.XHRResponse。在你的實施中,提供你自己的createRequest方法,並做我在1)中所說的。無論何時創建請求,請指定responseClass作爲您的自定義實現。

+0

好的,謝謝你,這幾乎是我期待的。選項1之前出現在我的腦海中,但我認爲這是我應該做的最後一件事。我會嘗試使用選項2. – starbugs 2012-04-24 12:17:04

+0

讓我知道它是如何工作的。 ;) – hvgotcodes 2012-04-24 12:56:33

+0

原來這兩個選項最終歸結爲相同的結果。您不僅必須重寫createRequest,還必須重寫invokeTransport和finishRequest。這甚至不包括錯誤處理,並且意味着很多重複的代碼。我認爲SC.Response類需要更多的抽象概念來提供一個乾淨的解決方案。 XDomainRequest不具有相同的屬性和回調,例如您需要實現onload而不是onreadystatechange,並且需要使用不同的回調來檢查錯誤。只要stackoverflow允許我回答我自己的問題,我會盡快發佈代碼。 – starbugs 2012-04-24 14:10:57

1

我已經根據hvgotcodes的建議快速編碼解決方案。下面的代碼實現了SC.XHRResponse的子類,該子類添加了處理特定於IE的XDomainRequest CORS請求所需的功能。請注意,這不會在使用XDomainRequest時在IE中處理錯誤。

MyApp.CorsRequest = SC.XHRResponse.extend({ 
    createRequest: function() { 
    function tryThese() { 
     for (var i=0; i < arguments.length; i++) { 
     try { 
      var item = arguments[i](); 
      return item; 
     } catch (e) {} 
     } 
     return NO; 
    } 

    return tryThese(
     function() { return new XDomainRequest(); }, 
     function() { return new XMLHttpRequest(); }, 
     function() { return new ActiveXObject('Msxml2.XMLHTTP'); }, 
     function() { return new ActiveXObject('Microsoft.XMLHTTP'); } 
    ); 
    }, 

    invokeTransport: function() { 
    var rawRequest, transport, handleReadyStateChange, async, headers; 

    rawRequest = this.createRequest(); 
    this.set('rawRequest', rawRequest); 

    // configure async callback - differs per browser... 
    async = !!this.getPath('request.isAsynchronous'); 

    if (async) { 
     if (!SC.browser.isIE && !SC.browser.isOpera) { 
     SC.Event.add(rawRequest, 'readystatechange', this, 
        this.finishRequest, rawRequest); 
     } else if(SC.browser.isIE) { 
      transport = this; 
     handleLoad = function() { 
      if (!transport) { return null; } 
      var ret = transport.finishRequest(); 
      if (ret) { transport = null; } 
      return ret; 
     }; 
     rawRequest.onload = handleLoad;   
     } else { 
     transport = this; 
     handleReadyStateChange = function() { 
      if (!transport) { return null; } 
      var ret = transport.finishRequest(); 
      if (ret) { transport = null; } 
      return ret; 
     }; 
     rawRequest.onreadystatechange = handleReadyStateChange; 
     } 
    } 

    // initiate request. 
    rawRequest.open(this.get('type'), this.get('address'), async); 

    // now send the actual request body - for sync requests browser will 
    // block here 
    rawRequest.send(this.getPath('request.encodedBody')) ; 
    if (!async) { this.finishRequest(); } 

    return rawRequest; 
    }, 

    finishRequest: function(evt) { 
    var rawRequest = this.get('rawRequest'), 
     readyState = rawRequest.readyState, 
     error, status, msg; 

     if (SC.browser.isIE) { 
      readyState = 4; 
      rawRequest.status = 200; 
     }  

    if (readyState === 4 && !this.get('timedOut')) { 
     this.receive(function(proceed) { 
     if (!proceed) { return; } 

     // collect the status and decide if we're in an error state or not 
     status = -1; 
     try { 
      status = rawRequest.status || 0; 
     } catch (e) {} 

     // if there was an error - setup error and save it 
     if ((status < 200) || (status >= 300)) { 

      try { 
      msg = rawRequest.statusText || ''; 
      } catch(e2) { 
      msg = ''; 
      } 

      error = SC.$error(msg || "HTTP Request failed", "Request", status); 
      error.set("errorValue", this) ; 
      this.set('isError', YES); 
      this.set('errorObject', error); 
     } 

     // set the status - this will trigger changes on related properties 
     this.set('status', status); 
     }, this); 

     // Avoid memory leaks 
     if (!SC.browser.isIE && !SC.browser.isOpera) { 
     SC.Event.remove(rawRequest, 'readystatechange', this, this.finishRequest); 
     } else { 
      if (window.XDomainRequest) 
       rawRequest.onload = null; 
      else 
      rawRequest.onreadystatechange = null; 
     } 

     return YES; 
    } 
    return NO; 
    } 
});