一、JSONP
JSONP原理
- 利用<script>标签没有跨域限制来达到与第三方通讯的目的。
- 当需要通讯时,本站脚本创建一个<script>元素,地址指向第三方的API网址,形如: <script src="http://www.example.net/api?callback=appendHTML"></script> 并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。
如上边的地址约定了回调函数为appendHTML( ),当第三方服务器检测到callback=appendHTML( ),会将数据打包到函数里返回:appendHTML(...数据...),使用方已经生命了appendHTML(),函数,所以返回的数据会执行。 - 第三方产生的响应为json数据的包装(故称之为jsonp,即json padding),形如: callback({"name":"hax","gender":"Male"}) 这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。
无后端服务器演示JSONP
- 以Windows为例,更改houts文件,地址为C:\Windows\System32\drivers\etc\housts
添加如下内容:
127.0.0.1 aa.zhihaoy.com
127.0.0.1 bb.zhihaoy.com
这样我们在127.0.0.1布置了两个域名
- 然后创建a.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaa</title>
</head>
<body>
<p id="aaa">aaa</p>
<script type="text/javascript">
function xxx (data) {
var p = document.getElementById('aaa')
p.innerText=('姓名:'+data.name+'\n'+'年龄:'+data.age)
}
</script>
<script type="text/javascript" src="http://bb.zhihaoy.com/test.txt"></script>
</body>
</html>
- 创建test.txt(不一定非是js格式,能够拿到数据就行,都会按js语法执行)
xxx({name:'jrengu',age:3})
- 开启本地服务器:(这里用的是80端口)
注释掉模拟执行请求的script标签,在浏览器地址栏输入http://aa.zhihaoy.com/a.html
页面显示: 测试02.png
还原模拟执行请求的script标签,刷新页面(请求的地址是:http://bb.zhihaoy.com/test.txt) 测试03.png 测试04.png
这样就跨域获得并执行了数据,关键在于双方约定的xxx( )函数,否则拿到数据也不执行。
server mock 演示 JSONP
- 创建a.html,为了和上边区分做了更改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaa</title>
</head>
<body>
<p id="aaa">aaa</p>
<script type="text/javascript">
function getWeather (data) {
var p = document.getElementById('aaa')
p.innerText=('杭州:'+data.weather)
}
</script>
<script type="text/javascript" src="http://127.0.0.1:8080/getWea?city=hangzhou&callBack=getWeather"></script>
//获取杭州天气
</body>
</html>
- mock第三方后端数据
- 在命令行启动本地服务器:
- 在地址栏输入:http://aa.zhihaoy.com/a.html(与第三方端口号不同)
得到:
尝试封装JSOP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaa</title>
</head>
<body>
<p id="aaa">aaa</p>
<script type="text/javascript">
function getWeather(data){
var p = document.getElementById('aaa')
p.innerText=('杭州:'+data.weather)
}
function jsonP (url,callBack,data) {
var script = document.createElement('script')
script.src = url+'?'+data+'&callBack='+callBack
document.head.appendChild(script)
document.head.removeChild(script)
}
jsonP ("http://127.0.0.1:8080/getWea",'getWeather','city=hangzhou')
</script>
</body>
</html>
测试后效果一样
二、CORS
原理:
在HTTP请求里添加特殊的头,允许服务器指定特定的域名可以跨域访问。
server mock 演示 CORS
- a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaa</title>
</head>
<body>
<input id="inta" type="button" value="获取天气">
<p>我是AAA</p>
<script type="text/javascript" src="myAJAX.js"></script>
<script type="text/javascript">
var btna = document.getElementById('inta')
var p = document.getElementsByTagName('p')[0]
btna.onclick = function () {
myAJAX('GET','http://127.0.0.1:8080/getWea?city=hangzhou',function(data){
p.innerText = '杭州:' + JSON.parse(data).weather
})
}
</script>
</body>
</html>
- router.js
/**
* 当 http://localhost:8080/getFriends 的GET请求到来时被下面匹配到进行处理
* 通过req.query获取请求的参数对象
* 通过 req.send发送响应
*/
router.get('/getWea', function(req, res) {
var city = req.query.city // 通过 req.query获取请求参数
var data
//根据请求参数mock数据
switch (city) {
case 'hangzhou':
data = {
weather: "多云"
}
break
case 'shanghai':
data = {
weather: "中雨"
}
break;
default:
data = {
weather: "无信息"
}
}
res.send(data)
})
-
默认情况不能跨域使用AJAX,在80端口打开a.html,去请求8080端口,得到:
测试09.png - 设置 header 可以处理跨域请求
设置后的router.js
router.get('/getWea', function(req, res) {
res.header('Access-Control-Allow-Origin', 'http://aa.zhihaoy.com')
//或者res.header('Access-Control-Allow-Origin', '*');
//设置 header
var city = req.query.city // 通过 req.query获取请求参数
var data
//根据请求参数mock数据
switch (city) {
case 'hangzhou':
data = {
weather: "多云"
}
break
case 'shanghai':
data = {
weather: "中雨"
}
break;
default:
data = {
weather: "无信息"
}
}
res.send(data)
})
刷新a.html得到:
测试10.png 测试11.png
三、降域document.domain
原理:
操作iframe,这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域同时为两个域设置document.domain= xxx.com
- a.html里有一个iframe(两者域名不同)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaa</title>
</head>
<body>
<input id="inta" type="text" placeholder="aaa">
<p>我是AAA</p>
<iframe src="http://bb.zhihaoy.com/b.html" width="300px" height="200px" style="border: 1px dotted"></iframe>
<script type="text/javascript">
//document.domain= 'zhihaoy.com' 默认不降域
var btna = document.getElementById('inta')
btna.oninput = function(){
window.frames[0].document.getElementById('intb').value = this.value
}
</script>
</body>
</html>
- b.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>bbb</title>
</head>
<body>
<input id="intb" type="text" placeholder="bbb">
<p>我是BBB</p>
<script type="text/javascript">
//document.domain= 'zhihaoy.com' 默认不降域
var btnb = document.getElementById('intb')
btnb.oninput = function(){
window.parent.document.getElementById('inta').value = this.value
}
</script>
</body>
</html>
-
不降域的情况下载a.html操作b.html会报错
测试12.png - 降域后则正常运行
postMessage
原理
postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
postMessage(data,origin)方法接受两个参数
1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
演示postMessage
- a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaa</title>
</head>
<body>
<input id="inta" type="text" placeholder="aaa">
<p>我是AAA</p>
<iframe src="http://bb.zhihaoy.com/b.html" width="300px" height="200px" style="border: 1px dotted"></iframe>
<script type="text/javascript">
var btna = document.getElementById('inta')
btna.oninput = function(){
window.frames[0].postMessage(this.value,'*')
}
window.addEventListener('message',function(e){
btna.value.e.data
})
</script>
</body>
</html>
- b.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>bbb</title>
</head>
<body>
<input id="intb" type="text" placeholder="bbb">
<p>我是BBB</p>
<script type="text/javascript">
var btnb = document.getElementById('intb')
btnb.oninput = function(){
window.parent.postMessage(this.value,'*')
}
window.addEventListener('message',function(e){
btnb.value=e.data
})
</script>
</body>
</html>
- a.html和b.html域名不同,但是可以互相传递数据
网友评论