cors
內(nèi)容回顧:
restful 規(guī)范
10個(gè)
除了 jsonp(它只可以發(fā) get 請(qǐng)求), 還有cors(可以發(fā)任何請(qǐng)求)。
解決跨域一般方法:加響應(yīng)頭-
VUE + rest framework 示例
- 登錄
- 課程列表
- 課程詳細(xì)
-
django 里面的content-type:
- django 的組件,幫我們做跨表的操作。
今日內(nèi)容
1. 跨域
- 瀏覽器的同源策略導(dǎo)致跨域。
瀏覽器的同源策略:瀏覽器會(huì)將請(qǐng)求攔截,對(duì) ajax 請(qǐng)求進(jìn)行阻攔。對(duì) href,src屬性都不阻攔。
ps:
小公司:靜態(tài)文件服務(wù)器:js,cs 之類的靜態(tài)都放在里面。
大公司:CDN
解決方案:
- 客戶端瀏覽器
- 請(qǐng)求的是 pythondv
- 發(fā)送的請(qǐng)求是 $.ajax(pythonav)
-------------------------------------
- 政府站點(diǎn) pythondv
-------------------------------------
- 我的站點(diǎn) pythonav
這個(gè)解決方案:
第一種:修改 pythondv 網(wǎng)站的請(qǐng)求方式,改成 script
第二種:政府站點(diǎn)請(qǐng)求方式不用修改,在 pythonav 拿到數(shù)據(jù)返回的時(shí)候,加上響應(yīng)頭。
一般就是你返回響應(yīng)頭,客戶端給你傳 cookie。
2. 解決跨域請(qǐng)求的兩種方式
- jsonp
- cors
3. 關(guān)于 jsonp
http://m.itdecent.cn/p/9b01fccf66b7
4. 關(guān)于 cors 跨域
隨著技術(shù)的發(fā)展,現(xiàn)在的瀏覽器可以主動(dòng)支持設(shè)置從而允許跨域請(qǐng)求,即:跨域資源共享(CORS,Cross-Origin Resource Sharing),其本質(zhì)是設(shè)置響應(yīng)頭,使得瀏覽器允許跨域請(qǐng)求。
整個(gè)CORS通信過(guò)程,都是瀏覽器自動(dòng)完成,不需要用戶參與。對(duì)于開發(fā)者來(lái)說(shuō),CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息,有時(shí)還會(huì)多出一次附加的請(qǐng)求,但用戶不會(huì)有感覺。
因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信。
4.1 簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求
條件:
1、請(qǐng)求方式:HEAD、GET、POST
2、請(qǐng)求頭信息:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type 對(duì)應(yīng)的值是以下三個(gè)中的任意一個(gè)
application/x-www-form-urlencoded
multipart/form-data
text/plain
注意:同時(shí)滿足以上兩個(gè)條件時(shí),則是簡(jiǎn)單請(qǐng)求,否則為復(fù)雜請(qǐng)求
4.2 兩者的區(qū)別
- 簡(jiǎn)單請(qǐng)求: 一次請(qǐng)求
- 非簡(jiǎn)單請(qǐng)求: 兩次請(qǐng)求,在發(fā)送數(shù)據(jù)之前會(huì)先發(fā)一次請(qǐng)求做'預(yù)檢',只有'預(yù)檢'通過(guò)后,才會(huì)再次發(fā)送一次請(qǐng)求用于數(shù)據(jù)傳輸。
4.3 關(guān)于預(yù)檢
- 請(qǐng)求方式:OPTIONS *****
- “預(yù)檢”其實(shí)做檢查,檢查如果通過(guò)則允許傳輸數(shù)據(jù),檢查不通過(guò)則不再發(fā)送真正想要發(fā)送的消息
- 如何“預(yù)檢”
> 如果復(fù)雜請(qǐng)求是PUT等請(qǐng)求,則服務(wù)端需要設(shè)置允許某請(qǐng)求,否則“預(yù)檢”不通過(guò)
Access-Control-Request-Method
> 如果復(fù)雜請(qǐng)求設(shè)置了請(qǐng)求頭,則服務(wù)端需要設(shè)置允許某請(qǐng)求頭,否則“預(yù)檢”不通過(guò)
Access-Control-Request-Headers
4.4 關(guān)于 cors 的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn): 可以發(fā)送任意請(qǐng)求,而 jsonp 只可以發(fā)送 get 請(qǐng)求。
- 缺點(diǎn): 當(dāng)前請(qǐng)求如果是復(fù)雜請(qǐng)求的時(shí)候得先做個(gè)預(yù)檢,再發(fā)真實(shí)的請(qǐng)求。發(fā)送兩次請(qǐng)求可能會(huì)有性能上的損耗。
5. jsonp 和 cors 的區(qū)別。
JSONP:服務(wù)端不用修改,需要改前端。發(fā)jsonp請(qǐng)求
JSONP:只能發(fā)GET請(qǐng)求
CORS:前端的代碼不用修改,服務(wù)端的代碼需要修改。如果是簡(jiǎn)單請(qǐng)求的話在服務(wù)端加上一個(gè)響應(yīng)頭。
CORS:可以發(fā)任意請(qǐng)求
6. 基于 cors 實(shí)現(xiàn) ajax 請(qǐng)求
6.1 支持跨域,簡(jiǎn)單請(qǐng)求。
服務(wù)器設(shè)置響應(yīng)頭: Access-Control-Allow-Origin = '域名' 或 '*'
<!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">
<title>Title</title>
</head>
<div>
<h1>welcome</h1>
<button onclick="getData()">獲取用戶數(shù)據(jù)</button>
</div>
<script src="/static/jquery-1.12.4.min.js"></script>
<script>
function getData() {
$.ajax({
url:'http://127.0.0.1:8080/index/',
type:"GET",
success:function (data) {
console.log(data)
}
})
}
</script>
</body>
</html>
from django.shortcuts import render
from django.http import JsonResponse
from rest_framework.views import APIView
# Create your views here.
class IndexView(APIView):
def get(self,request,*args,**kwargs):
ret = {
'code': 100,
'data': '777'
}
response = JsonResponse(ret)
response['Access-Control-Allow-Origin'] = "*"
return response
6.2 支持跨域,復(fù)雜請(qǐng)求。
- 如果是復(fù)雜請(qǐng)求在你真正的發(fā)請(qǐng)求之前,會(huì)先偷偷的發(fā)一個(gè)OPTION請(qǐng)求,先預(yù)檢一下,我允許你來(lái)你才來(lái)。如果要想預(yù)檢通過(guò),就需要寫個(gè) option 請(qǐng)求。
由于復(fù)雜請(qǐng)求時(shí),首先會(huì)發(fā)送“預(yù)檢”請(qǐng)求,如果“預(yù)檢”成功,則發(fā)送真實(shí)數(shù)據(jù)。
- “預(yù)檢”請(qǐng)求時(shí),允許請(qǐng)求方式則需服務(wù)器設(shè)置響應(yīng)頭:Access-Control-Request-Method
- “預(yù)檢”請(qǐng)求時(shí),允許請(qǐng)求頭則需服務(wù)器設(shè)置響應(yīng)頭:Access-Control-Request-Headers
- “預(yù)檢”緩存時(shí)間,服務(wù)器設(shè)置響應(yīng)頭:Access-Control-Max-Age
<!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">
<title>Title</title>
</head>
<body>
<input type="button" value="獲取用戶數(shù)據(jù)" onclick="getUser()">
<script src="/static/jquery-1.12.4.min.js"></script>
<script>
function getUser() {
$.ajax({
url:'http://127.0.0.1:8080/user/',
type:'POST',
data:{'k1':'v1'},
headers:{
'h1':'777777'
},
success:function (ret) {
console.log(ret)
}
})
}
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
class UserIndex(APIView):
def get(self,request,*args,**kwargs):
ret = {
'code': 100,
'data': 'ppp'
}
response = JsonResponse(ret)
response['Access-Control-Allow-Origin'] = "*"
return response
def post(self,request,*args,**kwargs):
print(request.POST.get('k1'))
ret = {
'code':101,
'data':'fffff',
}
response = JsonResponse(ret)
response['Access-Control-Allow-Origin'] = "*"
return response
def options(self, request, *args, **kwargs):
# self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
# self.set_header('Access-Control-Allow-Headers', "k1,k2")
# self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
# self.set_header('Access-Control-Max-Age', 10)
response = HttpResponse()
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Headers'] = 'h1'
# response['Access-Control-Allow-Methods'] = 'PUT'
return response
6.3 跨域獲取響應(yīng)頭
- 默認(rèn)獲取到的所有響應(yīng)頭只有基本信息,如果想要獲取自定義的響應(yīng)頭,則需要再服務(wù)器端設(shè)置Access-Control-Expose-Headers。
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p>
<input type="submit" onclick="XmlSendRequest();" />
</p>
<p>
<input type="submit" onclick="JqSendRequest();" />
</p>
<script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function XmlSendRequest(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
var result = xhr.responseText;
console.log(result);
// 獲取響應(yīng)頭
console.log(xhr.getAllResponseHeaders());
}
};
xhr.open('PUT', "http://c2.com:8000/test/", true);
xhr.setRequestHeader('k1', 'v1');
xhr.send();
}
function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'PUT',
dataType: 'text',
headers: {'k1': 'v1'},
success: function(data, statusText, xmlHttpRequest){
console.log(data);
// 獲取響應(yīng)頭
console.log(xmlHttpRequest.getAllResponseHeaders());
}
})
}
</script>
</body>
</html>
class MainHandler(tornado.web.RequestHandler):
def put(self):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('xxoo', "seven")
self.set_header('bili', "daobidao")
self.set_header('Access-Control-Expose-Headers', "xxoo,bili")
self.write('{"status": true, "data": "seven"}')
def options(self, *args, **kwargs):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Headers', "k1,k2")
self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
self.set_header('Access-Control-Max-Age', 10)
6.4 跨域獲取 cookie
- 在跨域請(qǐng)求中,默認(rèn)情況下,HTTP Authentication信息,Cookie頭以及用戶的SSL證書無(wú)論在預(yù)檢請(qǐng)求中或是在實(shí)際請(qǐng)求都是不會(huì)被發(fā)送。
如果想要發(fā)送:
- 瀏覽器端:XMLHttpRequest的withCredentials為true
- 服務(wù)器端:Access-Control-Allow-Credentials為true
- 注意:服務(wù)器端響應(yīng)的 Access-Control-Allow-Origin 不能是通配符 *
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p>
<input type="submit" onclick="XmlSendRequest();" />
</p>
<p>
<input type="submit" onclick="JqSendRequest();" />
</p>
<script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function XmlSendRequest(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
var result = xhr.responseText;
console.log(result);
}
};
xhr.withCredentials = true;
xhr.open('PUT', "http://c2.com:8000/test/", true);
xhr.setRequestHeader('k1', 'v1');
xhr.send();
}
function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'PUT',
dataType: 'text',
headers: {'k1': 'v1'},
xhrFields:{withCredentials: true},
success: function(data, statusText, xmlHttpRequest){
console.log(data);
}
})
}
</script>
</body>
</html>
class MainHandler(tornado.web.RequestHandler):
def put(self):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Credentials', "true")
self.set_header('xxoo', "seven")
self.set_header('bili', "daobidao")
self.set_header('Access-Control-Expose-Headers', "xxoo,bili")
self.set_cookie('kkkkk', 'vvvvv');
self.write('{"status": true, "data": "seven"}')
def options(self, *args, **kwargs):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Headers', "k1,k2")
self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
self.set_header('Access-Control-Max-Age', 10)