1-Django - 解决跨域的三种方式
环境:python3.6.2+Django1.11.5+pycharm2017.2.3
首先了解什么是浏览器同源策略?
1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同":
- 协议相同
- 域名相同
- 端口相同
不受浏览器同源策略影响的有,img,script,iframe,它们通过其src属性去请求数据,绕过浏览器同源策略,
所以,ajax也受限,但有些时候,我们不得不做一些手脚来达到ajax跨域请求操作,那么,就用到jsonp和cors了。
注意:cors和jsonp依赖于jquery,使用之前需要导入jquery。
jsonp方式:
我们在client端向server端发送一个普通的ajax请求,结果,会报一个:Access-Control-Allow-Origin的错误,但我们查看server端,是已经反回了,只是数据被浏览器拦截了,那么我们利用script的src的属性进行请求,
我们手动创建一个script标签, 传递URL,定义发送函数和接收变量的函数,但仅仅是这样的话,不做其他操作的话,会报错:数据未定义,因为,通过script请求过来的数据,会按照script的规则当做变量进行定义,所以,数据被script当做变量,导致报错
那么,知道如何报错,我们就手动给变量定义,callback=func,但是,这要server端配合对数据进行配合,不够灵活,因为两边的callback都要一致,client端变一下callback,还要告诉server端同步更改,为了解决这个,我们对server端更改代码,如示例所示,获取到client端的callback,再进行字符串格式化,这样,我们做好如此更改,不管client传什么,我们就可以一劳永逸了
这样,我们手动的实现了jsonp的原理
For exampl:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>ajax跨域之jsonp</h1> <input type="button" id="b1" value="向自己家发送请求"> <input type="button" id="b2" value="向别的网址发送请求"> <input type="button" id="b3" value="手动实现jsonp本质"> <input type="button" id="b4" value="手动实现jsonp本质callback"> <input type="button" id="b5" value="ajax的jsonp,自己写同名回调函数"> <input type="button" id="b6" value="ajax的jsonp,一切从简"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> // 向自己家发送请求没问题, $("#b1").click(function () { $.ajax({ url: 'http://127.0.0.1:8844/test1/', type: 'GET', success: function (arg) { alert(arg) } }) }); // 咱们向别的网址发送请求就会遇到问题,浏览器同源策略影响 $("#b2").click(function () { $.ajax({ url: 'http://127.0.0.1:8033/data/', type: 'GET', success: function (arg) { alert(arg) } }) }); /* jsonp的的本质,利用script标签的src属性发送请求 script的src属性把返回的数据当成一个未定义的变量,所以,我们还要对这个变量做处理 */ $("#b3").click(function () { var tag = document.createElement('script'); tag.src = 'http://127.0.0.1:8033/data2/'; document.head.appendChild(tag) }); // 利用自定义函数来获取返回的数据 function list(arg) { alert(arg); document.head.removeChild(tag) } $("#b4").click(function () { tag = document.createElement('script'); tag.src = 'http://127.0.0.1:8033/data3/?callback=list'; document.head.appendChild(tag) }); // ajax的jsonp,这两种方式,差不多,请求的网站只需动态加上回调函数 //方式一,在自定义的同名回调函数里处理数据,虽然success里也可以处理 function dict(arg) { alert(arg); } $("#b5").click(function () { $.ajax({ url: 'http://127.0.0.1:8033/data5/', type: 'GET', dataType:'JSONP', // 传输方式是jsonp jsonp:'callback', // 等于在地址栏后面加上:?callback jsonpCallback:'dict', // 等于http://127.0.0.1:8033/data4/?callback=dict }) }); // 方式二,一切从简,直接在ajax的success里处理数据 // 要是单独只说明datatype,则ajax会自己定义callback,但是所请求的网站包含数据的函数名,不能写死 // 需写动态的,因为ajax的callback是动态的, $("#b6").click(function () { $.ajax({ url: 'http://127.0.0.1:8033/data5/', type: 'GET', dataType:'JSONP', success: function (arg) { alert(arg) } }) }); </script> </body> </html>
from django.shortcuts import render,HttpResponse # Create your views here. def data(request): """ 正常返回,不要对数据做处理 """ return HttpResponse('数据66666') def data2(request): # 直接返回没有问题,但对方把数据当成未定义的变量 # return HttpResponse('数据66666') # 所以,我们要配合,比如下面这样, 但这样太复杂了 return HttpResponse('alert(111111)') def data3(request): import time time.sleep(2) data_obj = 'qwertytffasdf' response = request.GET.get('callback') print(response) return HttpResponse('%s("%s")'%(response,data_obj)) def data5(request): data_obj = 'aaaaaaaaaaaaaaaa' response = request.GET.get('callback') print(response) return HttpResponse('%s("%s")' % (response, data_obj)) # return HttpResponse('func("66666666")')
jsonp本质:
虽然依然通过ajax发送请求,但jsonp却偷偷地通过创建一个script标签进行数据的获取
过程是:在head头内建立script标签,获取到数据之后,立即把script标签删掉
注意:
如果在发送ajax时,只指定dataType:‘JSONP’,那么, jquery会自动的添加callback,如上面jsonp原理示例里面的方式二所示,我们无需再次定义一个函数去处理数据,直接在success里处理,如果按照方式一,可以不用写success这个回调函数,但是,还是推荐方式二,代码简洁;
问题:jsonp是否支持post?
不支持
jsonp只能发get请求
CORS(Cross-Origin Resource Sharing):跨站资源共享
cors实现思想:
CORS背后的基本思想是使用自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否
简单请求&&复杂请求
简单请求 OR 非简单请求 条件: 1、请求方式:HEAD、GET、POST 2、请求头信息: Accept Accept-Language Content-Language Last-Event-ID Content-Type 对应的值是以下三个中的任意一个 application/x-www-form-urlencoded multipart/form-data text/plain 注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求 简单请求和非简单请求的区别? 简单请求: 一次请求 非简单请求: 两次请求,在发送数据之前会先发一次请求用于做“预检”, 只有“预检”通过后才再发送一次请求用于数据传输。 关于“预检” - 请求方式:OPTIONS - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息 - 如何“预检” 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method="PUT" 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers="k1"
基于cors实现的ajax请求:
1、支持跨域的简单请求
服务器设置响应头:Access-Control-Allow-Origin = '指定域名'
服务器设置响应头:Access-Control-Allow-Origin = '*' # *:表示允许所有的请求
2、复杂请求的请求头:
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。 # 预检时可以发送空数据
“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method="PUT" # 或其他复杂请求如DELETE
“预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers="k1" # ajax的header:{"k1":"v1"}
“预检”缓存时间,服务器设置响应头:Access-Control-Max-Age=10 # 十秒内此请求再有请求过来时无需再发送options预检,直接发送复杂请求,减轻服务器压力
3、跨域获取响应头
默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。
4、跨域传输cookie
在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。
如果想要发送:
浏览器端:XMLHttpRequest的withCredentials为true
服务器端:Access-Control-Allow-Credentials为true
注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符
def cors(request): if request.method == 'OPTIONS': response = HttpResponse() # 设置缓存时间,在请求的十秒内,无需发送options请求预检验证, response['Access-Control-Max-Age'] = 10 # 只允许这个请求访问 # response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8844" # 允许所有请求来访问 response['Access-Control-Allow-Origin'] = "*" # 允许那些复杂的请求来访问 response['Access-Control-Allow-Methods'] = "PUT,DELETE" return response else: response = HttpResponse('CORS数据') response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8844" return response
如果要处理多个请求的跨域,可以在中间件内处理
from django.utils.deprecation import MiddlewareMixin class CorsMiddleware(MiddlewareMixin): """ 为解决跨域问题,所建立的中间件 """ def process_response(self, request, response): """ :param response: server端向client端返回的json数据 :return: 添加响应头 """ response["Access-Control-Allow-Methods"] = "POST,GET,OPTIONS" response['Access-Control-Allow-Origin'] = '*' response["Access-Control-Max-Age"] = '100000' response['Access-Control-Allow-Headers'] = "content-type" return response
requests:
另外的:requests模式:
导入Python的requests模块
requests.get("URL")
requests模式不受任意影响就能发送获取请求
总结:
三者的优缺点:
- cors相比jsonp支持更多的请求方式,
- jsonp需要client和server端的相互配合,
- jsonp比cors方式兼容性更好
- cors在client端无需设置,server端需要针对不同的请求,来做head头的处理
- jsonp和cors都是在本地执行请求数据,因为打开网页的时候,已经将js代码下到本地浏览器了,然后浏览器负责接下来的操作
- 而requests模式,则是浏览器向服务器发送请求,服务器根据请求,去拿数据,然后再返回给浏览器,增加服务器压力
测试代码:https://github.com/TestXX11/jsonp-cors
参考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html
参考博客:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html