python之路_JsonP解决跨域请求
一、同源策略
在我们开始阐述跨域请求之前我们需要弄清楚什么是同源策略?因为跨域请求就是为了解决同源策略的问题。好了我们开始正题吧!
首先我们需要知道同源这个概念:同源指的是一个请求路径中的请求协议、ip及端口和另一个请求路径中的请求协议、ip及端口保持一致。同源策略是指:它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。举个例子:我们在浏览器中打开一个网页,当我们点击页面某个连接执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和当前页面同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
如下一个实例,我们建立一个django项目:
页面ajax请求代码:
<script> $(".ajax_btn").click(function () { $.ajax({ url:"/send_ajax/", //当前项目中视图函数 success:function (data) { alert(data) } }) }) </script>
视图函数send_ajax:
def send_ajax(request): return HttpResponse("这是项目1 ajax返回")
此种为正常情况,同过页面的点击按钮发送ajax请求,请求的路径为当前项目下的某个视图函数,此种显然符合同源策略。但是当我们把上述的ajax请求路径更改为另一个项目某个视图函数的路径,如:url:"http://127.0.0.1:8002/send_ajax/",此两个项目的端口不同。此时再发送ajax请求时,浏览器端会报如下错误:
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8002/send_ajax/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
注意,上述这种错误是由于浏览器的同源策略给我们抛出的,实际上是请求到了对应的视图函数,只是浏览器不会把这种非同源请求返回的数据给我们。
二、跨域请求
如何解决上述的问题?这就是我要讲的跨域请求,其中跨域请求的主要原理就是利用script标签的跨域特性,如下,通过script标签跨域请求一个路径,就会得到相应的响应数据,如下就可以得到js文件的内容。
<script src="http://code.jquery.com/jquery-latest.js"></script>
1、jsonp介绍
jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。下面介绍一个jsonp基本实现原理,即利用script特性实现跨域请求,如下实例:
#项目1中html部分代码 <script> function func(name) { alert(name) } </script> <script src="http://127.0.0.1:8002/send_ajax/"></script> #项目2中的视图函数,即上述script标签请求的路径 def send_ajax(request): import json dic={"k1":"v1"} # return HttpResponse("func('yuan')") return HttpResponse("func('%s')" %json.dumps(dic))
如上为jsonp的基本实现原理。通过在页面定义一个回调函数,script标签远程远程请求一个跨域路径,跨越路径返回这个函数名,并将json数据形式以参数传递,页面script标签得到这一字符后,相当于调用了已定义的函数。
2、基于JS的JSONP的实现
一般情况下,我们希望这个script标签能够动态的调用,而不是像上面因为固定在html里面所以没等页面显示就执行了,很不灵活。我们可以通过页面的触发事件操作后,通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务。实例如下:
#项目1中html部分代码 <script> //回调函数 function bar(data) { alert(data); console.log(JSON.parse(data)) } </script> <script> //触发事件生成script标签 $(".ajax_btn").click(function () { script_request("http://127.0.0.1:8002/send_ajax/?callback=bar") //请求路径带回调函数名 }); //定义一个生成script标签的函数 function script_request(url) { var $script=$("<script>"); $script.attr("src",url); $("body").append($script); $("body script:last").remove() } </script> #项目2中的视图函数,即上述script标签请求的路径 def send_ajax(request): import json func_name=request.GET.get("callback") #获得回调函数的名字 dic={"k1":"v1"} return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))
为了更加灵活,上述我们将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调。
3、基于jquery的JSONP的实现(一)
具体实例如下:
//项目1中html中部分代码 <script> $(".ajax_btn").click(function () { $.getJSON("http://127.0.0.1:8002/send_ajax/?callback=?",function (data) { alert(data); console.log(data) }) }); </script>
//项目2中的视图函数,即跨域请求的路径
def send_ajax(request):
import json
func_name=request.GET.get("callback") #获得回调函数的名字
dic={"k1":"v1"} print("ok")
return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))
如上,jQuery框架也当然支持JSONP,可以使用$.getJSON(url,[data],[callback])方法。与js实现的方式相比,我们并不要自己生成一个script标签,客户端也并不需要自己定义一个回调函数,要注意的是在$.getJSON(url,[data],[callback])方法的url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数名。
4、基于jquery的JSONP的实现(二)
上述这种方法,很方便,不需要我们自己定义回调函数和指定回调函数名,但是,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?我们可以使用$.ajax方法来实现。如下例:
//项目1中html中部分代码 <script> $(".ajax_btn").click(function () { $.ajax({ url:"http://127.0.0.1:8002/send_ajax/", dataType:"jsonp", //相当于script标签 jsonp:'callbacks', //相当于路径中回调函数路径参数键值对的键 jsonpCallback:"func" //相当于路径中回调函数路径参数键值对的值,回调函数名 }) }); //定义回调函数 function func(arg) { alert(arg); console.log(arg) } </script>
//项目2中的视图函数,即跨域请求的路径 def send_ajax(request): import json func_name=request.GET.get("callbacks") #获得回调函数的名字 dic={"k1":"v1"} print("ok") return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))
5、基于jquery的JSONP的实现(三)
在上小节中jsonp: 'callbacks'就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名,server端接受callback键对应值后就可以在其中填充数据打包返回。但是,jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,那前端就得用回调函数来处理对应数据了。利用jQuery可以很方便的实现JSONP来进行跨域访问。
//项目1中html中部分代码 <script> $(".ajax_btn").click(function () { $.ajax({ url:"http://127.0.0.1:8002/send_ajax/", dataType:"jsonp", jsonp:'callbacks', success:function (data) { alert(data); console.log(data) } }) }); </script> //项目2中的视图函数,即跨域请求的路径 def send_ajax(request): import json func_name=request.GET.get("callbacks") #获得回调函数的名字 dic={"k1":"v1"} print("ok") return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))
三、跨域请求应用
如某合作单位提供如下获取数据接口:http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403,请通过跨域请求获取数据。由数据连接可以知对方已经定义了回调函数的函数名,即为list,所以我们可以通过上述"基于jquery的JSONP的实现(二)"的方式获取数据,结果如下:
<script> $(".ajax_btn").click(function () { $.ajax({ url:"http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403", dataType:"jsonp", jsonp:"callback", jsonpCallback:"list" }) }); function list(result) { console.log(result); } </script>
然后自行可以根据数据结果类型进行处理。