我们知道,由于同源规则的限制,普通的 Ajax 交互并不能直接进行跨域请求。
但是在实际开发中,有些时候我们又必须对数据进行跨域传输。实际上,这类技术已经被广泛应用,很多网站的流量统计和分析工具都使用了类似的方法,我本人之前的文章中也介绍过相关技术。
今天要讲的是:
JSON 数据的跨域 Ajax 传输和回调。
实际工作中,我们可能会遇到这样的问题。
例如,和服务器进行数据交换,展示级联下拉菜单。最典型的就是全国各个地区的选择:
用户选择一个地区之后,再根据用户的选择,在下一个下拉框中展示这个地区下面的其他地区。
例如:

如果把这些数据全部以数组的方式存放在前端,一方面会增加前台的数据量,另一方面也不利于后期的维护和扩展。所以,我们可以考虑把数据放到服务器上,根据用户的需求进行异步加载。这里实际上有两种情况。
第一种是服务器直接返回数据。
第二种是客户端获取服务器返回的数据之后,再进行处理。
实际开发中,第二种情况更加常见。下面就以地区选择这个例子来说明。
前台代码
<!DOCTYPE HTML>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>JSON实现跨域AJAX</title>
</head>
<body>
<script>
function getCity(p){
if(p == ""){
return;
}
var json = document.createElement("script");
json.id = "jsondata";
json.src =
"http://www.test.com/getCity.php" +
"?provice=" + p +
"&callback=callback";
document.body.appendChild(json);
}
function callback(data){
var city = document.getElementById("city");
var cityvar = "";
for(var i in data){
cityvar += "<option>" + data[i] + "</option>";
}
city.removeAttribute("disabled");
city.innerHTML = cityvar;
document.body.removeChild(
document.getElementById("jsondata")
);
}
</script>
<select
id="provice"
onchange="getCity(this.options[this.selectedIndex].value)"
>
<option value="">请选择</option>
<option value="hunan">湖南</option>
<option value="henan">河南</option>
<option value="shanghai">上海</option>
</select>
<select id="city" disabled="disabled">
</select>
</body>
</html>
服务器端的 PHP 代码如下:
<?php
header("Content-Type:text/javascript");
$provice = $_GET["provice"];
$callback = $_GET["callback"];
$area["henan"] =
'["郑州","开封","洛阳","平顶山","安阳","鹤壁","新乡","焦作","濮阳","许昌","三门峡"]';
$area["hunan"] =
'["长沙","常德","岳阳","浏阳","郴州","益阳","怀化","湘潭","株洲","湘西自治区"]';
$area["shanghai"] =
'["浦东新区","黄浦区","卢湾区","徐汇区","长宁区","静安区","普陀区","闸北区","虹口区"]';
echo $callback . "(" . $area[$provice] . ")";
?>
实现原理
原理其实比较简单。
当用户选择省份以后,JavaScript 动态创建一个 script 标签,然后把相应的数据传递给服务器。例如:
http://www.test.com/getCity.php?provice=hunan&callback=callback
服务器收到请求之后,根据省份返回相应的数据。
返回的内容实际上并不是单纯的 JSON,而是一段可以直接执行的 JavaScript:
callback(["长沙","常德","岳阳", ...])
由于前台已经定义好了:
functioncallback(data){
...
}所以浏览器加载服务器返回的脚本之后,就会直接执行这个回调函数。
整个过程可以简单理解成:
选择省份
↓
动态创建 script
↓
跨域请求服务器
↓
服务器返回 callback(JSON)
↓
浏览器执行 callback()
↓
更新城市下拉菜单
这里比较重要的一点就是:
浏览器对 script 标签的加载并没有普通 Ajax 那样的同源限制。
因此,可以通过动态插入 script 标签的方式完成跨域数据请求。
这种方式使用起来比较方便,也比较容易进行扩展。
需要注意的问题
另外还需要注意一点。因为服务器返回的内容中包含回调函数,所以这种传输方式如果处理不当,非常容易引发 XSS 问题,需要特别注意数据的安全处理。
除此之外,上面的代码还有很多可以优化的地方。例如,为了减少重复请求和节省带宽,前台最好对已经获取过的数据进行缓存。
例如第一次选择湖南的时候获取一次:
之后再次选择湖南,就没有必要再次向服务器请求,可以直接使用之前已经获取的数据。这样可以进一步减少请求数量。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。