本文详细解析如何利用iframe嵌套机制解决Ajax跨域请求问题。通过主域页面嵌入目标域iframe,再由目标域iframe嵌入主域代理页面,利用URL参数传递数据与回调函数名,最终通过window.top实现跨域数据回传,完整展示了从参数传递到结果返回的全流程代码实现。
iframe跨域通信原理概述
在前端开发中,Ajax请求通常受限于同源策略,即只能访问与当前页面域名、协议和端口相同的资源。若尝试跨域请求,浏览器会抛出异常并阻止请求执行,这主要是出于安全考量。尽管存在限制,但仍可通过特定方案实现跨域访问,例如利用iframe进行Ajax跨域请求。


具体架构如示意图所示:域a.com下的request.html页面中嵌入一个指向域b.com的response.html的iframe,而response.html内部又嵌入了域a.com下的proxy.html。

实现跨域通信的核心逻辑是:域a.com的request.html通过URL参数将请求数据传递给域b.com的response.html,由response.html向同域的process.php发起真正的Ajax请求。随后,response.html将处理结果通过URL传递给域a.com的proxy.html。由于proxy.html与request.html同域,proxy.html可通过window.top对象将结果直接返回给request.html,从而完成跨域通信。
该流程的关键在于,真正的Ajax请求发生在域b.com内部,而非发起方域a.com。域a.com主要执行两个操作:一是由request.html负责向域b.com发送包含参数的请求;二是由proxy.html负责将域b.com返回的数据回传给request.html。

跨域访问流程图
代码实现与流程解析
接下来展示具体的代码实现,首先明确文档结构如下:
http://a.com/
request.html
proxy.html
http://b.com/
response.html
process.php
首先分析request.html的代码,为便于理解,JavaScript代码直接嵌入页面中:
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>该页面的路径是:http://a.com/request.html</title>
</head>
<body>
<p id="result">这里将会填上响应的结果</p>
<a id="sendBtn" href="javascript:void(0)">点击,发送跨域请求</a>
<iframe id="serverIf"></iframe>
<script type="text/javascript">
document.getElementById("sendBtn").onclick = function() {
var url = "http://b.com/response.html";
var fn = "GetPerson";//这是定义在response.html的方法
var reqdata = '{"id" : 24}';//这是请求的参数
var callback = "CallBack";//这是请求全过程完成后执行的回调函数,执行最后的动作
CrossRequest(url, fn, reqdata, callback);//发送请求
}
function CrossRequest(url, fn, reqdata, callback) {
var server = document.getElementById("serverIf");
server.src = url + "?fn=" + encodeURIComponent(fn) + "&data=" + encodeURIComponent(reqdata) + "&callback=" + encodeURIComponent(callback);//这里由request.html向response.html发送的请求其实就是通过iframe的src将参数与回调方法传给response.html
}
function CallBack(data) {//回调函数
var str = "My name is " + data.name + ". I am a " + data.sex + ". I am " + data.age + " years old.";
document.getElementById("result").innerHTML = str;
}
</script>
</body>
</html>结合代码与注释可知,该页面的核心意图是指示response.html执行其内部定义的GetPerson方法,并传入参数'{"id" : 24}'。关于为何将CallBack函数名传递给response.html,是因为该函数定义在当前页面,response.html无法直接执行。后续代码显示,response.html仅负责将该回调函数名传递给proxy.html,而proxy.html因与request.html同域,可直接调用该回调函数。
response.html处理逻辑
接下来查看response.html的代码实现:
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>该页面的路径是:http://b.com/response.html</title>
</head>
<body>
<iframe id="proxy"></iframe>
<script type="text/javascript">
function _request(reqdata, url, callback) {//通用方法,ajax请求
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var data = xmlhttp.responseText;
callback(data);
}
}
xmlhttp.open("POST", url);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xmlhttp.send(reqdata);
}
function _getQuery(key) {//通用方法,获取url参数
var query = location.href.split("?")[1];
var value = decodeURIComponent(query.split(key + "=")[1].split("&")[0]);
return value;
}
function GetPerson(reqdata, callback) {//向process.php发送ajax请求
var url = "process.php";
var fn = function(data) {
var proxy = document.getElementById("proxy");
proxy.src = "http://b.com/Proxy.html?data=" + encodeURIComponent(data) + "&callback=" + encodeURIComponent(callback);
}
_request(reqdata, url, fn);
}
(function() {
var fn = _getQuery("fn");
var reqdata = _getQuery("data");
var callback = _getQuery("callback");
eval(fn + "('" + reqdata +"', '" + callback + "')");
})();
</script>
</body>
</html>该部分代码的主要功能是:接收来自request.html的请求参数与方法名,向服务器process.php发起Ajax请求,并将服务器返回的数据及从request.html传来的回调函数名传递给proxy.html。
process.php后端处理
随后查看process.php的代码,实现较为简单:
代码如下:
<?php
$data = json_decode(file_get_contents("php://input"));
header("Content-Type: application/json; charset=utf-8");
echo ('{"id" : ' . $data->id . ', "age" : 24, "sex" : "boy", "name" : "huangxueming"}');
?> 这段PHP代码逻辑直观,具备基础PHP知识即可理解。
proxy.html结果回传
最后分析proxy.html的代码:
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>该页面的路径是:http://a.com/proxy.html</title>
</head>
<body>
<script type="text/javascript">
function _getUrl(key) {//通用方法,获取URL参数
var query = location.href.split("?")[1];
var value = decodeURIComponent(query.split(key + "=")[1].split("&")[0]);
return value;
}
(function() {
var callback = _getUrl("callback");
var data = _getUrl("data");
eval("window.top." + decodeURIComponent(callback) + "(" + decodeURIComponent(data) + ")");
})()
</script>
</body>
</html>这是通信的最后一步:proxy.html从URL中获取由request.html经response.html传递过来的回调函数名及响应数据,并通过window.top调用request.html中定义的回调函数,完成数据回传。
在实际应用中,proxy.html可封装为通用代理组件,无需频繁修改。若需支持多种跨域方法,可在域a.com中定义相应接口,而域b.com则提供类似GetPerson这样的接口供调用。此外,实际部署时通常需隐藏iframe。最后强调:掌握技术本身并非最关键,具备学习能力更为重要。