跨域解决方案之JSONP
什么是jsonp
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。为什么我们从其它网站获取数据需要通过jsonp技术来获取呢?那是因为同源策略。
什么是同源策略
同源策略,它是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
所谓同源,即域名,协议,端口相同。
所谓跨域,即不同域,ajax不允许跨域。
例如:
http://www.baidu.com和https://www.baidu.com
http://www.baid.com和http://www.baidu.com:8080
jsonp的原理
整体来说,jsonp实现跨域的原理是跨域的服务端把客户端所需要的数据放进客户端本地的一个js方法里,进行调用,客户端在本地的js对返回的数据进行处理。这样就实现了不同域名下的两个站点间的交流。
由于<script>标签的src可以跨域,利用这一点,就有了jsonp这种非正式传输协议。因为有可能是多个不同站点都要访问这个服务端,那么各个站点要调用的方法可能是各不相同的,如果把方法名写死的话,就会很不和谐。所以解决的办法是各个站点来访问服务端时,在url中带一个参数(callback(cb))(根据情况)过来,服务端获取到这个参数,就会在生成js代码时,以这个callback参数作为方法名,再把数据放到这个方法里。这样各个站点就可以调用各自的方法了。这也是jsonp的一个要点。
jsonp的实现方式
下面举一个例子来验证ajax不支持跨域及跨域解决。
通过点击页面发起ajax请求,请求成功后,将后台的数据显示。
首先通过<script>标签引入封装好的ajax文件。
封装的ajax如下:
function ajax(options){
// 1.处理默认参数
var {type,url,success,error,data,timeout} = options;
type = type || "get";
data = data || {};
timeout = timeout || 2000;
// 2.解析要发送的数据
var str = "";
for(var i in data){
str += `${i}=${data[i]}&`;
}
// 3.根据方式,决定是否处理url
if(type == "get"){
var d = new Date();
url = url + "?" + str + "__qft=" + d.getTime();
}
// 4.开启ajax
var xhr = new XMLHttpRequest();
// 注意:open中的方式
xhr.open(type,url,true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
// 5.执行成功之前,先判断是否传入
success && success(xhr.responseText);
// 成功之后,不应有失败
error = null;
}else if(xhr.readyState == 4 && xhr.status != 200){
// 6.执行失败之前,先判断是否传入
error && error(xhr.status);
// 失败之后,不应有成功
success = null;
// 且失败不应多次执行
error = null;
}
};
// 7.如果请求超时,执行失败
setTimeout(() => {
error && error("timeout");
// 失败之后,不应有成功
success = null;
}, timeout);
// 8.最后根据type的方式,决定send的发送内容和格式
if(type == "post"){
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send(str)
}else{
xhr.send()
}
}
接下来是PHP代码:
<?php
$u = @$_REQUEST["user"];
$p = @$_REQUEST["pass"];
echo "这是前端的ajax发送的数据,现在再还给前端:".$u."-------".$p;
?>
首先来同域的情况,当点击页面的时候,在控制台打印出后台的数据。
<body>
解决ajax的跨域问题
</body>
<script src="../ajax.js"></script>
<script>
document.onclick= function () {
var url="http://localhost/1908/jsonp/data/data.php";
ajax({
url:url,
data:{
user:"admin",
pass:"123456"
},
success: function (res) {
console.log(res);
}
})
}
</script>
如果将搜索栏中的localhost改成27.0.0.1或只修改php的url地址改为127.0.0.1,则会报错。(请求的资源上不存在访问控制允许源头,即出现了跨域。)
解决的方式通过动态创建<script>标签来实现。具体操作如下:
首先引入封装好的jsonp.js文件。
function jsonp(url,success,data){
// 1.处理默认参数
data = data || {};
// 2.解析数据
var str = "";
for(var i in data){
str += `${i}=${ data[i] }&`;
}
// 3.创建script标签,设置src,准备开启jsonp
var script = document.createElement("script");
script.src = url + "?" + str;
document.body.appendChild(script);
// 4.定义全局函数
// window.asdasdasd = function(res){
// window["asdasdasd"] = function(res){
// window[data.callback] = function(res){
// window[data["callback"]] = function(res){
window[data[data.columnName]] = function(res){
success(res);
}
}
PHP代码:
<?php
$u = @$_REQUEST["user"];
$p = @$_REQUEST["pass"];
$c= @$_REQUEST["cb"];
echo $c."('这是前端的ajax发送的数据,现在再还给前端:".$u."-------".$p."')";
?>
前端操作代码:
<script src="../jsonp.js"></script>
<script>
document.onclick= function () {
var url="http://127.0.0.1/1908/jsonp/data/data.php";
jsonp(url, function (res) {
console.log(res);
},{
user:"admin",
pass:"123456",
//用来保存后台接收的回调函数名的字段名,为了给自己封装的函数传参,防止多次修改封装好的函数
columnName:"cb",
//根据后台要接收的字段名,发送回调函数名,已经无所谓取什么了
cb:"abc"
});
};
</script>
通过以上操作,及解决了跨域访问的问题。
实现效果如下:
注意:jsonp不是ajax。
jsonp为什么不支持POST?
1、因为 JSONP 是通过动态创建 script 实现的
2、创建 script 只能发送 get 请求