精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
尽管构建动态的Web应用程序非常实用,但XMLHttpRequest (jQuery的Ajax实现背后的底 层浏览器技术)常常会受到严格限制。为了防止各种跨站点脚本攻击,一般情况下从提供原始页 面的服务器之外的站点请求文档是不可能的。
这通常都是一种积极的情形。例如,对接收到的JSON数据,可以调用eval()来解析(相对 而目,jQuery.parseJSON()更安全一些)。如果数据文件中存在恶意代码,那么通过eval() 解析就会执行这些恶意代码。不过,JavaScript的安全模型会限制数据文件必须与网页保存在相同的服务器上,这样就可以保证数据的可靠性。
但是,从第三方来源中加载数据往往是很有必要的。因而,也有许多方式可以绕过上述安全限制,即能够实现通过Ajax请求取得其他站点的数据。
其中一种方法是通过服务器加载远程数据,然后在客户请求时提供给浏览器。这是一种非常有效的手段,因为服务器能够对数据进行预处理。例如,可以从几个来源加载包含RSS新闻的XML文件,然后在服务器上将这些XML文件聚合到一个源文件中,当请求发生时再将这个新文件发布给客户。
如果想不通过服务器的参与加载远程地址中的数据,那我们就必须聪明一些。例如,加载外 来JavaScript文件的一种流行方法是根据请求注入<script>标签。由于jQuery能帮我们插人新的 DOM兀素,因此向文档中注人<script>标签非常简单:
$(document.createElement('script')) .attr('src', 'http://example.com/example.js') .appendTo('head');
实际上,$.getScript()方法在检测到其URL参数中包含远程主机时,就会自动采用这种技术;也就是说,该方法已经替我们想到了这一点。
此时,浏览器会执行加载的脚本,但却没有任何机制能够从脚本中取得结果。为此,使用这 种技术要求同远程主机进行协作。加载的脚本必须执行某些操作,例如设置一个对本地环境有影响的全局变量。而远程主机上的服务除了发布能够通过这种方式执行的脚本外,还会提供一个 API以便同远程脚本进行交互。
另一种方法是使用<iframe>这个HTML标签来加载远程数据。可以S<iframe>兀素指定任 何URL作为其获取数据的来源,包括与提供页面的服务器不匹配的URL。因此,第三方服务器上的数据能够轻易地加载到<iframe>*,并在当前页面上显示出来。然而,要操作<1^&^16>中的数据,仍然存在同使用<script>标签时一样的协作需求;位于<iframe>*的脚本需要明确地向父文档中的对象提供数据。
使用<script>标签从远程获取JavaScript文件的思路,可以变通为从其他服务器取得JSON 文件。不过,这样需要对服务器上的JSON文件稍加修改。在实现这一技术的众多解决方案中, jQuery直接支持的是JSONP (JSON with Padding,填充式JSON )。
JSONP的格式是把标准JSON文件包装在一对圆括号中,圆括号又前置一个任意字符串。这 个字符串,即所谓的P( Padding,填充),由请求数据的客户端来决定。而且,由于有一对圆括号,因此返回的数据在客户端可能会导致一次函数调用,或者是为某个变量赋值一取决于客户端请求中发送的填充字符串。
用PHP在服务器端实现对JSONP的支持非常简单:
<?php print($_GET['callback'] $data ?>
这里,$data是一个包含JSON文件字符串表示的变量。调用这段脚本时,从客户端请求中 取得的callback查询字符串参数,会被添加到包含JSON数据文件的圆括号前面。
为演示这一技术,需要稍微修改一下代码清单6-6中的JSON示例,以便调用这个远程数据源。 $.getJSON()函数利用了一个特殊的占位符?来实现这一点,参见代码清单6-20。
代码清单6-20
$(document).ready(function() { var url = ,http://examples.learningjquery.com/jsonp/g.php,; $('#letter-g a').click(function(event) { event.preventDefault(); $.getJSON(url + ,?callback=?', function(data) { var html =''; $.each(data, function(entryIndex, entry) { html += '<div class="entry">'; html += '<h3 class="term">' + entry.term + '</h3>'; html += '<div class="part">' + entry.part + '</div>'; html += '<div class="definition">'; html += entry.definition; if (entry.quote) { html += '<div class="quote">'; $.each(entry.quote, function(lineIndex, line) { html += '<div class=Mquote-lineM>' + line + '</div>'; }); if (entry.author) { html += '<div class="quote-author">' + entry.author + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; }); $('#dictionary').html(html); }); }); });
正常情况下,我们是不能从远程服务器(这个例子中的examples.learningjquery.com) 取得JSON数据的。但是,由于远程文件经过设置以JSONP格式提供数据,因此通过在URL后面添加一个查询字符串,并使用?作为callback参数的占位符就可以获得数据。请求返回之后, jQuery会为我们替换解析结果并通过data参数将数据传人成功函数。结果就好像是在处理本 地JSON数据一样。
注意: 前面提到的安全注意事项在这里也适用,即服务器返回的任何结果都将在用户的计算机中执行。因此,应该只针对来自可信任站点的数据使用JSONP技术。