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进行的向百度发送请求,这种情况就是跨域请求,浏览器会禁止接收数据
<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函数,关键是函数名一致
<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
function f1(arg) {
alert(arg);
}
common2
f1('hello'); // 传递的参数hello
JSONP实现
通过点击发送标签,js在head创建script标签,script访问http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403,
返回的数据存放在浏览器的内存中
返回数据是list(),就是执行list函数。参数是返回值中的内容
<!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
//list和返回的名称一致
function list(arg) {
console.log(arg);
}
结果:
传入的参数是字典类型的,返回的是对象
jsonp 应用--API
模拟两个网站,一个是提供数据的网站api,数据是json格式的,设置域名是www.s4.com,端口8001
另一个www.s5.com,要访问s4的数据
存放api的网站
def users(request):
# v = request.GET.get('callback')
# print('请求is coming')
user_list = [
'aaa',
'bbb',
'ccc'
]
return HttpResponse(json.dumps(user_list))
需要在setting中设置允许的host,否则报错
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访问是不能获取数据的,同源策略会阻止
def jsonp(request):
return render(request, 'jsonp.html')
1 使用XMLHttpresponse被拒绝
在s4的网站已经获取到了请求,但是浏览器同源策略禁止接收返回的数据
<!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端对数据进行拼接。
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端相互定义好接口
<!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,然后进行拼接。
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函数
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)
自己的页面
<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'
<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是一种规范
jsonp:'callback',
jsonpCallback:'list',
<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解决的是跨域问题