Django跨域请求--JSONP
一 需求分析
之前我们学习的都是在本站内进行数据的获取,创建Django web1服务,简单程序代码如下:
urls.py
urlpatterns = [
path('web1/', views.web1),
]
views.py
def web1(request):
return HttpResponse('web1数据')
访问结果如下:
补充:我们也可以在终端通过requesrts模块进行数据获取
那么我们怎么实现跨域请求呢?现在我试着通过目前掌握的知识进行如下尝试:
二 小试牛刀
现在在原有基础上新建一个Django web2服务:
urls.py
urlpatterns = [
path('web2/', views.web2),
]
views.py
def web2(request):
return render(request,'web2.html')
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
</body>
</html>
访问结果:
因为同时开启两个web服务端,所以需要注意端口号的修改
接下来该怎么处理呢?
方式一:通过后端请求
views.py
def web2(request):
import requests
res = requests.get('http://127.0.0.1:8000/web1/').text
return render(request,'web2.html',{'res':res})
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
<P>{{ res }}</P>
</body>
</html>
结果:
访问简图如下:
方式二:浏览器通过Ajax请求
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
<script src="/static/jquery-3.2.1.js"></script>
<script>
$.ajax({
url:'http://127.0.0.1:8000/web1/',
type:'GET',
success:function(arg){
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}
})
</script>
</body>
</html>
当然,以上Ajax部分我们也可以通过原生Ajax实现。
通过谷歌浏览器访问出现以下错误:
再通过火狐浏览器访问:
这是为什么呢?
这是因为浏览器存在同源策略(Same origin policy)机制,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。
现在所有支持JavaScript的浏览器都会使用这个策略。
所谓同源是指域名,协议,端口相同。
当一个浏览器的两个tab页中分别打开来百度和谷歌的页面,当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收(百度百科)。
访问简图如下:
怎么绕过浏览器的同源策略?这里我们就需要引入JSONP了。
三 JSONP实现跨域请求
浏览器同源策略并不是对所有的请求均制约:
- 制约: XmlHttpRequest
- 不制约: img、iframe、script等具有src属性的标签
JSONP(JSONP - JSON with Padding是JSON的一种“使用模式”),利用<script>标签的src属性(浏览器允许script标签跨域)完成跨域。代码如下:
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
</body>
<script src="/static/jquery-3.2.1.js"></script>
<script src="http://127.0.0.1:8000/web1/"></script>
</html>
执行结果如下:
根据上面的结果,证明数据我们已经拿到了,但是没有渲染在页面上,报错提醒该值未定义:这是因为我们使用<script>标签取到的数据只是一个单纯的文本格式,加载了js代码中,并没有声明变量去接收它,所以在js中则会报错。
其实质上是在内存中加入了返回的字符串,在前端的结果为:
<script src="http://127.0.0.1:8000/web1/">
web1数据
</script>
所以,只是单纯的返回一个文本并没有什么意义,我们需要的是的数据。解决如下:
web1服务:
views.py
def web1(request):
l = 'a = "web1数据"'
return HttpResponse('%s' % (l))
web2服务:
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
<script src="/static/jquery-3.2.1.js"></script>
<script src="http://127.0.0.1:8000/web1/"></script>
</body>
</html>
访问结果如下:
这样有一个问题,是什么呢?我们不知道第三方何时将数据返回,要是有一种机制,第三方返回后,前端浏览器自动执行就好了,方法如下:
web1服务:
views.py
def web1(request):
import json
# return HttpResponse("func('web1数据')")
l = {'name':'joe1991','age':18} #返回数据可以是文本,也可以是字典及其他格式
return HttpResponse("func('%s')" % json.dumps(l)) #返回一个func()字符串,客户端的js中存在func函数,自然会去执行func函数,arg是传的形参。
web2服务:
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
<script src="/static/jquery-3.2.1.js"></script>
<script>
//创建一个函数,当第三方返回请求数据数据时,自动执行。当然,第三方需返回一个与之名称相同且包含数据的字符串
function func(arg) {
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}
</script>
<script src="http://127.0.0.1:8000/web1/"></script>
</body>
</html>
执行结果如下:
这其实就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。
于此同时,我们希望这个<script>标签能够动态的调用,同时,为了更加灵活,我们可以在访问第三方地址时将回调函数的函数名传送给第三方,第三方则会返回以我们自定义的回调函数名的方法,将获取的json数据传入这个方法完成回调:
web1服务:
views.py
def web1(request):
import json
func_name = request.GET.get('callback') #获取回调函数名称
# return HttpResponse("%s('web1数据')"%func_name)
l = {'name':'joe1991','age':18}
return HttpResponse("%s('%s')" %(func_name,json.dumps(l)))
web2服务:
web2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
<input type="button" onclick="f1()" value="发送JSONP请求"></input>
<script src="/static/jquery-3.2.1.js"></script>
<script>
function func(arg) {
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}
function f1() {
jsonp('http://127.0.0.1:8000/web1/?callback=func')
}
//js实现<script>动态化
function jsonp(url) {
var tag = document.createElement('script');
tag.setAttribute("type","text/javascript");
tag.src = url;
document.body.appendChild(tag);
document.body.removeChild(tag);
}
//jQuery实现<script>动态化
// function jsonp(url) {
// var $tag = $("<script>");
// $tag.attr("src",url);
// $tag.attr("id","scr1");
// $("body").append($tag);
// $("body").remove($tag);
// }
</script>
</body>
</html>
四 jQuery对JSONP的实现
jQuery框架当然也支持JSONP,可以使用$.getJSON(url,[data],[callback])方法
web2中web2.html做如下修改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>web2数据</h3>
<input type="button" onclick="f1()" value="发送JSONP请求"></input>
<script src="/static/jquery-3.2.1.js"></script>
<script>
function f1() {
$.getJSON('http://127.0.0.1:8000/web1/?callback=?', function(arg) {
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}); //function为匿名函数
}
</script>
</body>
</html>
注意:在url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个?是内部自动生成的一个回调函数名。
另外,如果我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名,我们可以使用$.ajax方法来实现。
其他代码一致,仅function f1() 不同,f1()代码如下:
function f1() {
$.ajax({
url:'http://127.0.0.1:8000/web1/',
type:'GET', //可省略
dataType:'JSONP', //必须有,告诉第三方,这次访问要的是一个jsonp的结果
jsonp: 'callback', //键
jsonpCallback: 'func', //函数的名字,url中为:.../?callback=func,此时需创建func函数
})
}
function func(arg) {
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}
当然,最简单的形式还是通过回调函数来处理:
代码一:
function f1() {
$.ajax({
url:'http://127.0.0.1:8000/web1/?callback=?', //函数名自动生成,也可以在下面单独列举
type:'GET', //可省略
dataType:'JSONP', //必须有,告诉第三方,这次访问要的是一个jsonp的结果
success:function(arg){
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}
})
}
代码二:
function f1() {
$.ajax({
url:'http://127.0.0.1:8000/web1/',
type:'GET', //可省略
dataType:'JSONP', //必须有,告诉第三方,这次访问要的是一个jsonp的结果
jsonp: 'callback', //后台获取request.GET.get('callback'),url中为:.../?callback=?
success:function(arg){
var $tag = $('<p>');
$tag.text(arg);
$('h3').append($tag);
}
})
}
jsonp: 'callback' 就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名'func',server端接受callback键对应值后就可以在其中填充数据打包返回了;
jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,前端就可以用回调函数来处理对应数据了。利用jQuery可以很方便的实现JSONP来进行跨域访问。
五 总结
说明:JSONP只是一种方式,目的是解决跨域问题,其他语言也存在。
5.1 浏览器与第三方服务端约定
客户端:
1.通过GET方式向后台发送回调函数(可指定或自动生成)—URL/?callback=xxx
2.需创建function xxx(arg){},或使用回调函数
第三方服务端:
1.获取函数名称:funcname = request.GET.get(callback)
2.返回:funcname(‘数据’)
5.2 JSONP的使用
1.自已动态创建script(第三部分)
2.通过jQuery实现(第四部分)
5.3 JSONP优缺点
优点:
1.通过jsonp的传输方式我们可以绕开浏览器同源策略
2.页面不用刷新而获得数据
缺点:
1.第三方服务端需要将数据封装在函数中
2.本地浏览器需要定义被封装的函数
3.JSONP只能发送GET请求
一句话概述JSONP:通过<script>标签的跨域特性绕过同源策略(创建一个回调函数,然后在第三方服务上得到这个函数名称,并将返回数据json化后作为这个函数的形参返回给前端,完成回调)。












浙公网安备 33010602011771号