2012-04-09 62 views
20

從另一個服務器/域解析XML時,我該如何解決跨域問題?有人能給我一個例子嗎?這個例子不一定僅限於jQuery,因爲JavaScript也足夠了。如何在jQuery中解析XML跨域?

+0

你是什麼意思crossdomain – 2012-04-09 04:43:38

+1

只允許來自JavaScript的跨域執行是JSONP。 – AlienWebguy 2012-04-09 04:45:19

+0

「腳本和JSONP請求不受相同的源策略限制。」 – 2012-04-09 04:54:48

回答

66

爲了充分理解爲什麼純粹的跨域XML不起作用,它有助於首先了解如何促進跨域JSON。

首先,讓我們看看會發生什麼,當你在做的jQuery AJAX請求:

$.ajax({ 
    url: '/user.php?userId=123', 
    success: function(data) { 
     alert(data); // alerts the response 
    }); 

在上面的例子中,AJAX請求時相對於域。我們知道,如果我們試圖在路徑之前添加一個不同的域,請求將失敗並出現安全異常。

但是,這並不是說瀏覽器無法向其他域發出請求。下面是可能你熟悉的一個例子:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 

根據我們對如何導入頁面上的JavaScript知識,我們可以看到,它可以加載另一個域中存在的資源!

JSONP是一個利用這些知識的概念。 JSONP代表「帶填充的JSON」,它的成功取決於JavaScript對象可以使用字符串表示法以及JavaScript腳本標記可以從外部域加載和運行內容的事實。

引擎蓋下,jQuery的JSONP看起來是這樣的,雖然它可能不準確:

// programmatically load a script tag on the page using the given url 
function loadRemoteData(url) { 
    var script = document.createElement("script"); 
    script.setAttribute("type","text/javascript"); 
    script.setAttribute("src", url); 
    document.getElementsByTagName("head")[0].appendChild(script); 
} 

此外,頁面上的某個地方,我們定義了一個回調處理程序:

function processData(jsonResult) { 
    alert(JSON.stringify(jsonResult)); //alert the JSON as a string 
} 

這裏,我們提出要求:

// make a request for the data using the script tag remoting approach. 
loadRemoteData("http://example.com/users.php?userId=123&callback=processData"); 

爲了正常工作,我們的PHP腳本必須都以JSON格式返回數據,並且還必須以JavaScript函數名稱的形式在字符串周圍添加「填充」,我們可以將其作爲參數傳遞給它們。 「回調」)

因此,從服務器的響應可能會是這個樣子,如果我們看它在Firebug的或Chrome NET標籤:

processData({ "userId" : "123" , "name" : "James" , "email" : "[email protected]" }); 

因爲我們知道JavaScript內容運行作爲一旦它被下載,我們之前定義的我們的processData函數立即被調用並且傳遞我們的JSON字符串作爲參數。然後通知使用JSON.stringify將對象轉換回字符串。

因爲它是一個對象,我也可以訪問它的屬性,像這樣:

function processData(jsonResult) { 
    alert(JSON.stringify(jsonResult)); //alert the JSON as a string 

    // alert the name and email 
    alert("User name is " + jsonResult.name + " and email is " + jsonResult.email); 
} 

最後,讓我們移動到的主要問題:能JSONP被用來獲取XML,或者我們可以解析XML跨-域?答案,正如其他人所指出的那樣,是一個響亮的NO,但讓我們來看看爲什麼用一個例子:

processData(<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>[email protected]</email></user>); 

現在,如果原始XML被傳遞到功能會發生什麼?它會中斷,因爲JavaScript無法將XML轉換爲JSON。

但是,假設我們把XML引號:

processData("<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>[email protected]</email></user>"); 

現在,在這個例子中,jsonResult變量實際上需要一個字符串,我們可以一起工作。使用一些JavaScript XML解析實用程序,我們可以將該字符串加載到XML DOM解析器中,並使用它進行操作!

但是,它不是純粹的XML,它仍然是一個JavaScript引擎。來自PHP服務器的響應類型仍然是text/javascript,我們仍然使用script標籤來加載真正的JavaScript。總之,你可以使用「XMLP」或帶有填充的XML(我剛剛提出,它不是真的!),但是如果你要去經歷所有的實際修改你的響應的麻煩返回一個函數回調包裝器,你也可以將你的輸出轉換爲JSON,並讓瀏覽器自動處理轉換並本地處理轉換,並節省你必須使用XML解析器的麻煩。

但是,如果由於某種原因,將數據保存爲XML格式會更容易,您可以修改響應併爲其提供JavaScript封裝。

我認爲這很有用的一些情況可能是您的XML數據來自存儲在數據庫中的遺留應用程序,並且您使用腳本標記遠程處理或JSONP調用將其返回給客戶端。

+8

非常方便,+1。你值得讚揚 – defau1t 2012-12-23 11:56:28

4

我發現了一個很好的解決方案來從跨域ajax請求中檢索xml。

由於jQuery 1.5你可以使用dataType「jsonp xml」(http://api.jquery.com/jQuery.ajax/)!

所以我用這個:

$.ajax({ 
      type: "GET", 
      url: "http://yoururl", 
      dataType: "jsonp xml", 
      success: function(xmlResponse) { // process data } 
     }); 

服務器端我的WebServices的我用於封裝的jQuery創建的回調中的XML字符串結果:

private static Stream GetXmlPStream(string result, string callback) 
     { 
      if (result == null) 
       result = string.Empty; 

      result = EncodeJsString(result); 

      if (!String.IsNullOrEmpty(callback)) 
       result = callback + "(" + result + ");"; 

      byte[] resultBytes = Encoding.UTF8.GetBytes(result); 

      if (WebOperationContext.Current != null) 
       WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml"; 
      return new MemoryStream(resultBytes); 
     } 

和魔術方法(我發現它在另一個堆棧線程),你需要清理你的XML字符串(所以JavaScript可以解析它):

private static string EncodeJsString(string s) 
     { 
      StringBuilder sb = new StringBuilder(); 
      sb.Append("\""); 
      foreach (char c in s) 
      { 
       switch (c) 
       { 
        case '\"': 
         sb.Append("\\\""); 
         break; 
        case '\\': 
         sb.Append("\\\\"); 
         break; 
        case '\b': 
         sb.Append("\\b"); 
         break; 
        case '\f': 
         sb.Append("\\f"); 
         break; 
        case '\n': 
         sb.Append("\\n"); 
         break; 
        case '\r': 
         sb.Append("\\r"); 
         break; 
        case '\t': 
         sb.Append("\\t"); 
         break; 
        default: 
         int i = (int)c; 
         if (i < 32 || i > 127) 
         { 
          sb.AppendFormat("\\u{0:X04}", i); 
         } 
         else 
         { 
          sb.Append(c); 
         } 
         break; 
       } 
      } 
      sb.Append("\""); 

      return sb.ToString(); 
     } 

希望這會有所幫助!

+9

當我嘗試這個時,我得到了'Uncaught SyntaxError:Unexpected token <'錯誤,例如:'$ .get('http://www.webservicex.net/geoipservice.asmx/GetGeoIPContext',function(data) {console.log(data);},'jsonp xml');' – 10basetom 2016-01-29 05:00:07

+2

這隻有當你有權訪問服務器代碼時纔有用。 – 2016-08-02 16:25:11