【问题标题】:How to Parse XML Cross-domain in jQuery?如何在 jQuery 中解析 XML 跨域?
【发布时间】:2014-06-20 04:45:59
【问题描述】:

从不同的服务器/域解析 XML 时,我将如何解决跨域问题?有人可以给我一个例子吗?该示例不必仅限于 jQuery,因为 JavaScript 也可以。

【问题讨论】:

  • 跨域是什么意思
  • 只有 JSONP 允许从 JavaScript 跨域执行。
  • "脚本和 JSONP 请求不受同源策略限制。"
  • 在哪里可以看到 JSONP 的好例子?

标签: jquery xml


【解决方案1】:

要完全理解为什么跨域 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" : "example@example.com" } );

因为我们知道 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>example@example.com</email></user>);

现在,如果将原始 XML 传递给函数会发生什么?它将中断,因为 JavaScript 无法将 XML 转换为 JSON。

但是,假设我们将 XML 放在引号中:

processData("<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>example@example.com</email></user>");

现在,在这个例子中, jsonResult 变量实际上接受了一个字符串,我们可以使用它。使用一些 JavaScript XML 解析实用程序,我们可以将该字符串加载到 XML DOM 解析器中并使用它来处理!

但是,它不是纯 XML,它仍然是底层的 JavaScript 响应。 PHP 服务器的响应类型仍然是 text/javascript,我们仍然使用脚本标签来加载真正只是纯 JavaScript 的内容。

总而言之,您可以使用“XMLP”或带填充的 XML(我只是编造的,这不是真的!),但是如果您要经历实际修改响应以返回的所有麻烦一个函数回调包装器,您也可以将输出转换为 JSON,让浏览器自动本地处理转换,省去使用 XML 解析器的麻烦。

但如果出于某种原因将数据保存为 XML 格式更容易,您可以修改响应并为其提供 JavaScript 包装器。

如果您将遗留应用程序的 XML 数据存储在数据库中,并且您使用脚本标记远程处理或 JSONP 调用将其返回到客户端,那么我认为这很有用。

【讨论】:

  • 这非常方便,+1。你值得称赞
【解决方案2】:

我找到了一个很好的解决方案,可以从跨域 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 }
        });

我用于将 xml 字符串结果封装在 jQuery 创建的回调中的 Web 服务的服务器端:

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);
        }

还有一个神奇的方法(我在另一个 Stack 线程中找到),你需要清理你的 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();
        }

希望这会有所帮助!

【讨论】:

  • 我在尝试这个时遇到了Uncaught SyntaxError: Unexpected token &lt; 错误,例如:$.get('http://www.webservicex.net/geoipservice.asmx/GetGeoIPContext', function(data) { console.log(data); }, 'jsonp xml');
  • 这仅在您有权访问服务器代码时才有用。
【解决方案3】:

我意识到这是一个老问题,但我在搜索时发现了这个问题。而且,我认为答案是针对与此处发布的问题略有不同的问题,因此我想添加此答案,该答案至少应适用于 jQuery 1.12 及更高版本。我没有在早期版本中测试过。

好的,我要请求这个网址:http://sample.domain/feeds/itemdata.xml

我想在其中找到Item,如下所示:

<Item>
  <ProductItemNo>1228101530</ProductItemNo>
  ...
</Item>

这可行,跨域:

$.ajax({
  dataType: "xml", 
  url: "http://sample.domain/feeds/itemdata.xml", 
  success: function(xml) {
    var itemdata = $(xml).find("ProductItemNo:contains('1228101530')").parent();
  }
});

【讨论】:

  • 这对我跨域不起作用。如果您只是尝试使用其他方法检索它,我会遇到与您相同的错误:No 'Access-Control-Allow-Origin' header is present on the requested resource.
猜你喜欢
  • 2014-03-18
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多