利用iframe突破子域名Ajax请求阻塞

作者:工具松鼠

时间:2026-10-22

浏览:621

本文探讨如何解决浏览器对同域Ajax请求数量的限制导致的阻塞问题。通过引入隐藏的iframe并设置相同的document.domain,可以在不同子域名间建立通信通道,从而绕过主域名的请求限制,实现优雅且高效的跨子域名异步数据交互。

文章目录
javascript0.jpg

经过排查,问题根源已找到。虽然解决过程看似简单,但这反映出我在相关知识储备上仍有不足。以下是具体的解决方案。



被阻塞的Ajax请求

被阻塞的Ajax请求



首先,我们需要验证请求是否存在阻塞现象。使用以下代码进行测试:



连续发起三个请求


代码实现如下:

function simpleRequest()
{  
var request = new XMLHttpRequest();
request.open('POST', 'Script.ashx');
request.send(null);
} 
function threeRequests()
{  
simpleRequest();
simpleRequest();
simpleRequest();
} 

当调用threeRequests函数时,会连续向同一域名发起三个请求。通过性能监控图表可以清晰地观察到阻塞效果:


script1.jpg

后续请求受前序请求阻塞



每个请求耗时约1.5秒。显然,第三个请求必须等待前两个请求全部结束后才能执行,导致总耗时超过3秒。我们的目标就是消除这种阻塞现象。



传统的跨域名异步请求方案

传统的跨域名异步请求方案


Ajax安全机制的核心限制之一,便是禁止跨域名(Cross-Domain)的Ajax请求。除非用户手动开启本地文件访问权限或在IE中放宽跨域限制,否则向其他域名发起Ajax请求均会被浏览器拦截。此外,跨域判断极为严格:不同的子域名或相同域名但不同端口的资源,均被视为独立域名,无法直接发起Ajax请求。



表面上看,这一限制难以突破,但iframe提供了另一种思路。



尽管iframe并非HTML标准的核心部分,但因其实用性,Firefox等浏览器也予以支持(类似情况还有innerHTML)。目前网上存在一些通过iframe实现跨域异步请求的方案,但实现质量参差不齐。其基本原理是:在目标域名下放置一个代理页面(Proxy),主页面通过Query String将请求参数传递给iframe中的代理页面;代理页面执行Ajax请求后,将结果写入自身location的hash中;主页面则轮询iframe src的hash值,一旦检测到变化,便从hash中提取所需信息。



该方案实现复杂且功能受限。在IE和Firefox中,URL长度通常限制在2000字符左右。对于普通需求或许足够,但若需传输大量数据,则显得力不从心。与下文提出的方案相比,其唯一优势在于支持任意域名的跨域异步请求,而我们的方案仅能突破子域名限制。



接下来介绍我们的解决方案。



 

优雅地突破子域名限制

优雅地突破子域名限制



突破子域名限制的关键依然在于iframe。



iframe具有独特优势,允许我们跨子域名访问iframe内的页面对象,包括window和DOM结构,甚至调用JavaScript(通过window对象)。只需将主页面与iframe内页面的document.domain设置为相同值即可。随后,在不同子域名的页面中发起请求,并通过JavaScript传递结果。唯一需要的额外资源是一个简单的静态代理页面。



下面开始编写原型代码,虽然简单,但足以说明原理。



首先,编写一个静态页面作为iframe内的代理,代码如下:



SubDomainProxy.html


代码实现如下:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Untitled Page</title>
<script type="text/javascript" language="javascript">
document.domain = 'test.com'; function sendRequest(method, url)
        {  
var request = new XMLHttpRequest();
request.open(method, url);
request.send(null);
        }  
</script>
</head>
<body>
</body>
</html>


 

接着编写主页面代码:




http://www.test.com/Default.html



代码实现如下:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript" language="javascript">
document.domain = 'test.com'; function simpleRequest()
        {  
var request = new XMLHttpRequest();
request.open('POST', 'Script.ashx');
request.send(null);
        }  
function crossSubDomainRequest()
        {  
var proxy = document.getElementById('iframeProxy').contentWindow;
proxy.sendRequest('POST', 'http://sub0.test.com/Script.ashx');
        }  
function threeRequests()
        {  
simpleRequest();
simpleRequest();
crossSubDomainRequest();
        }  
</script>
</head>
<body>
<input type="button" value="Request" onclick="threeRequests()" />
<iframe src="http://sub0.test.com/SubDomainProxy.html" style="display:none;"
id="iframeProxy"></iframe>
</body>
</html>

当执行threeRequests方法时,将同时向http://www.test.com和http://sub0.test.com两个不同子域名下的资源发起请求。从性能图表可以看出,最后一个请求不再受前两个请求的阻塞:


script2.jpg

不同子域名的请求互不阻塞



结果令人满意。



尽管该方案仅能突破子域名限制,但这已足够应对大多数场景。我们为何非要追求任意域名间的异步通信呢?何况我们的方案实现更为优雅。在后续文章中,我们将为ASP.NET AJAX客户端实现完整的CrossSubDomainRequestExecutor,自动判断是否为跨子域名请求并选择相应的Ajax请求方式。这样,客户端异步通信层将对开发人员完全透明。这难道不是最令人愉快的事情吗?:)



注意事项

注意事项



以下几点值得注意:



经过多次尝试,我发现创建XMLHttpRequest对象、调用open方法和send方法,都必须在iframe内的页面中执行,才能在IE和Firefox中成功发送Ajax请求。



在上述示例中,向子域名请求的路径为http://sub0.test.com/Script.ashx。请注意,完整的子域名不可省略,否则在Firefox下会出现权限不足的错误,调用open方法时会抛出异常——Firefox似乎将其视为父页面域名的资源。


受浏览器安全策略影响,浏览器禁止不同域(如phinest.org和lab.phinest.org)、不同协议(如http://phinest.org和https://phinest.org)、不同端口(如http://phinest.org和http://phinest.org:8080)下的页面通过XMLHttpRequest相互访问。此限制同样影响不同页面间JavaScript的相互调用与控制。然而,当主域、协议、端口相同时,通过设置页面的document.domain主域,JavaScript可以在不同子域名间进行访问控制。例如,设置document.domain='phinest.org'后,http://phinest.org和http://lab.phinest.org页面可互访。这一特性也为该情况下不同子域名下的XMLHttpRequest相互访问提供了解决方案。



对于主域、协议、端口相同的Ajax跨域问题,早期已有通过设置document.domain解决的说法,但鲜见具体成功案例。本次尝试的原理是:利用一个隐藏的iframe引入所跨另一子域的页面作为代理,通过JavaScript控制iframe引入的另一子域的XMLHttpRequest进行数据获取。对于不同主域、不同协议或不同端口下的Ajax访问,仍需通过后台代理实现。



实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章