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); 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
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"; document.head.appendChild(tag); } </script> </body> </html>
|
common
1 2 3 4
| 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): 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): 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('varname') 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') 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'; document.head.appendChild(tag); } 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', dataType:'JSONP', jsonp:'callback', jsonpCallback:'list', success:function (arg) { console.log(arg) } }); } </script>
|
JSONP总结:
- 是一种约定
- 自己写动态添加script
- 使用jQuery的jsonp
- jsonp只能发送GET请求
- jsonp解决的是跨域问题