JSONP的原理及应用

JSONP 的原理及应用

浏览器的同源策略:ajax跨域发送请求,浏览器不接受数据

但是带有src属性的可以跨域,如script,JSONP就是利用的这种方式

同源策略参考:

https://www.zhihu.com/question/25427931/answer/30848852
https://www.zhihu.com/question/31459669
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy

JSONP 技术

jsonp是一种技术,一种绕过跨域的技巧

禁止ajax跨域

下面是使用ajax进行的向百度发送请求,这种情况就是跨域请求,浏览器会禁止接收数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<a onclick="sendMsg();">发送</a>
<script src="/static/jquery-3.2.1.js"></script>
<script>
function sendMsg() {
$.ajax({
url:'https://www.baidu.com',
type:'GET',
success:function (arg) {
console.log(arg);//使用ajax(本质是使用的XMLHttprequest) 浏览器的同源策略会禁止接收不同域名的内容
/*
XMLHttpRequest cannot load https://www.baidu.com/.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://127.0.0.1:8000' is therefore not allowed access.
*/
}
})
}
</script>

jsonp原理 js代码的执行

jsonp利用scipt标签的src可以跨域

在jsonp的head中创建两个js,第一个是js函数,第二个是执行js函数,关键是函数名一致

1
2
3
4
5
6
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/common.js"></script>
<script src="/static/common2.js"></script>
</head>

程序从上往下执行,函数f1,后面执行函数f1并传递参数hello,最终的结果就是页面显示hello

common

1
2
3
function f1(arg) {
alert(arg);
}

common2

1
f1('hello'); // 传递的参数hello

JSONP实现

通过点击发送标签,js在head创建script标签,script访问http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403,

返回的数据存放在浏览器的内存中

返回数据是list(),就是执行list函数。参数是返回值中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/common.js"></script>
//最终就是在这里创建的script标签
</head>
<body>
<a onclick="sendMsg();">发送</a>
<script>
function sendMsg() {
var tag = document.createElement('script'); //创建标签
tag.src ="http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403";//这里面的返回的数据是list() 里面的数据是字典的形式
document.head.appendChild(tag); //找到head标签并添加
}
</script>
</body>
</html>

common

1
2
3
4
//list和返回的名称一致
function list(arg) {
console.log(arg);
}

结果:

传入的参数是字典类型的,返回的是对象

jsonp 应用–API

模拟两个网站,一个是提供数据的网站api,数据是json格式的,设置域名是www.s4.com,端口8001

另一个www.s5.com,要访问s4的数据

存放api的网站

1
2
3
4
5
6
7
8
9
def users(request):
# v = request.GET.get('callback')
# print('请求is coming')
user_list = [
'aaa',
'bbb',
'ccc'
]
return HttpResponse(json.dumps(user_list))

需要在setting中设置允许的host,否则报错

1
2
DisallowedHost at /
Invalid HTTP_HOST header: 'www.s4.com:8001'. You may need to add 'www.s4.com' to ALLOWED_HOSTS.

访问api的网站

这个网站定义成www.s5.com:8000 端口8000,访问s4,使用AJAX的XMLHttpresponse访问是不能获取数据的,同源策略会阻止

1
2
def jsonp(request):
return render(request, 'jsonp.html')

1 使用XMLHttpresponse被拒绝

在s4的网站已经获取到了请求,但是浏览器同源策略禁止接收返回的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="获取用户列表" onclick="getUsers();">
<ul id="user_list">
</ul>
<script src="/static/jquery-3.2.1.js"></script>
<script>
function getUsers() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
var content = xhr.responseText;
console.log(content);
}
};
xhr.open('GET','http://www.s4.com:8001/users/');
xhr.send();
}
</script>
</body>
</html>

2 jsonp解决方案

被访问的网站s4

之前返回的数据是[] s5 访问的时候获取到的数据存放在浏览器的内存中,这样就没法获取。最好的方式就是通过变量名获取数据。
这主要返回的数据是带有名字的。下面在s4端对数据进行拼接。

1
2
3
4
5
6
7
8
9
10
11
def users(request):
# v = request.GET.get('callback')
print('请求is coming')
user_list = [
'aaa',
'bbb',
'ccc'
]
user_list_str = json.dumps(user_list)
temp = "a=%s" % (user_list_str) # 对数据进行拼接
return HttpResponse(temp)

3 增加灵活性

上面的方式会有变量名重复的问题,而且是全局变量

下面通过get请求的时候直接发送变零名

自己的在get请求中定义vername是aaa,这个是任意的。关键是要和API端相互定义好接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="获取用户列表" onclick="getUsers();">
<script>
function getUsers () {
var tag = document.createElement('script');
tag.src = 'http://www.s4.com:8001/users/?varname=aaa'; //自己定义传输的名字
document.head.appendChild(tag);
}
</script>
</body>
</html>

被访问的s4 需要获取get请求中的vername,然后进行拼接。

1
2
3
4
5
6
7
8
9
10
11
12
def users(request):
# v = request.GET.get('callback')
v = request.GET.get('varname') # 获取get请求中的vername
print('请求is coming')
user_list = [
'aaa',
'bbb',
'ccc'
]
user_list_str = json.dumps(user_list)
temp = "%s=%s" % (v,user_list_str)
return HttpResponse(temp)

4 服务端返回数据的时间是不确定的

上面的已经实现了自己定义返回的数据名,但是服务端返回数据的时间是不确定。特别是返回时间特别长的时候,不能立刻获取数据。

下面在s4服务端进行修改

关键是temp = “%s(%s)” % (v,user_list_str)

返回的数据是aaa()的形式,用户获取数据后,有aaa函数,直接执行aaa函数

1
2
3
4
5
6
7
8
9
10
11
def users(request):
v = request.GET.get('callback') # 获取callback
print('请求is coming')
user_list = [
'aaa',
'bbb',
'ccc'
]
user_list_str = json.dumps(user_list)
temp = "%s(%s)" % (v,user_list_str) # 巧妙的方式
return HttpResponse(temp)

自己的页面

1
2
3
4
5
6
7
8
9
10
11
<script>
function getUsers () {
var tag = document.createElement('script');
tag.src = 'http://www.s4.com:8001/users/?callback=aaa';//aaa是函数名
document.head.appendChild(tag);
}
// 返回数据是aaa() 也就是直接执行下面的函数
function aaa(arg) {
console.log(arg)
}
</script>

总结:

客户端

  • URL?callback=xxxx
  • function xxxx(arg){}

服务端

  • 获取 funcname = request.GET.get(callback)
  • 返回: funcname(….)

5 使用jQuery的jsonp

上面的方式是我们自己定义并在页面添加了script,jQuery内部也实现了jsonp

加上dataType=’JSONP’

1
2
3
4
5
6
7
8
9
10
11
<script src="/static/jquery-3.2.1.js"></script>
<script>
$.ajax({
url:'http://www.s4.com:8001/users/?callback=aaa',
type:'GET',
dataType:'JSONP',
success:function (arg) {
console.log(arg)
}
});
</script>

6 jsonp 的其他参数

下面的两个参数会自动的在URL上添加成

url:'http://www.s4.com:8001/users/?callback=list'

返回值和函数名是可以修改的,定义成callback和list是一种规范

1
2
jsonp:'callback',
jsonpCallback:'list',
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script src="/static/jquery-3.2.1.js"></script>
<script>
function getUsers() {
$.ajax({
url:'http://www.s4.com:8001/users/',
type:'GET', //只能发送get请求 即便是改成post是发个get请求
dataType:'JSONP',
jsonp:'callback',
jsonpCallback:'list',
success:function (arg) {
console.log(arg)
}
});
}
</script>

JSONP总结:

  • 是一种约定
  • 自己写动态添加script
  • 使用jQuery的jsonp
  • jsonp只能发送GET请求
  • jsonp解决的是跨域问题
© 2018 Peter's Blog Center All Rights Reserved.
Theme by hiero