09.Django基礎七之Ajax
一 Ajax簡介
1.簡介
AJAX(Asynchronous Javascript And XML)翻譯成中文就是“非同步的Javascript和XML”。即使用Javascript語言與伺服器進行非同步互動,傳輸的資料為XML(當然,傳輸的資料不只是XML,現在更多使用json資料)。
AJAX 不是新的程式語言,而是一種使用現有標準的新方法。
AJAX 最大的優點是在不重新載入整個頁面的情況下,可以與伺服器交換資料並更新部分網頁內容。(這一特點給使用者的感受是在不知不覺中完成請求和響應過程)
AJAX 不需要任何瀏覽器外掛,但需要使用者允許JavaScript在瀏覽器上執行。
a.同步互動:客戶端發出一個請求後,需要等待伺服器響應結束後,才能發出第二個請求;
b.非同步互動:客戶端發出一個請求後,無需等待伺服器響應結束,就可以發出第二個請求。
AJAX除了非同步的特點外,還有一個就是:瀏覽器頁面區域性重新整理;(這一特點給使用者的感受是在不知不覺中完成請求和響應過程
2.示例
頁面輸入兩個整數,通過AJAX傳輸到後端計算出結果並返回。
html檔名稱為ajax_demo1.html,內容如下
<!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>AJAX區域性重新整理例項</title> </head> <body> <input type="text" id="i1">+ <input type="text" id="i2">= <input type="text" id="i3"> <input type="button" value="AJAX提交" id="b1"> <script src="/static/jquery-3.2.1.min.js"></script> <script> $("#b1").on("click", function () { $.ajax({ url:"/ajax_add/", //別忘了加雙引號 type:"GET", data:{"i1":$("#i1").val(),"i2":$("#i2").val()}, //object型別,鍵值形式的,可以不給鍵加引號 success:function (data) { $("#i3").val(data); } }) }) </script> </body> </html>
views.py裡面的內容:
def ajax_demo1(request): return render(request, "ajax_demo1.html") def ajax_add(request): #time.sleep(10) #不影響頁面傳送其他的請求 i1 = int(request.GET.get("i1")) i2 = int(request.GET.get("i2")) ret = i1 + i2 return JsonResponse(ret, safe=False) #return render(request,'index.html') #返回一個頁面沒有意義,就是一堆的字串,拿到了這個頁面,你怎麼處理,你要做什麼事情,根本就沒有意義
urls.py裡面的內容
urlpatterns = [
...
url(r'^ajax_add/', views.ajax_add),
url(r'^ajax_demo1/', views.ajax_demo1),
...
]
啟動django專案,然後執行看看效果,頁面不重新整理
3.AJAX常見應用情景
搜尋引擎根據使用者輸入的關鍵字,自動提示檢索關鍵字。
還有一個很重要的應用場景就是註冊時候的使用者名稱的查重。
其實這裡就使用了AJAX技術!當檔案框發生了輸入變化時,使用AJAX技術向伺服器傳送一個請求,然後伺服器會把查詢到的結果響應給瀏覽器,最後再把後端返回的結果展示出來。
a.整個過程中頁面沒有重新整理,只是重新整理頁面中的區域性位置而已!
b.當請求發出後,瀏覽器還可以進行其他操作,無需等待伺服器的響應!
當輸入使用者名稱後,把游標移動到其他表單項上時,瀏覽器會使用AJAX技術向伺服器發出請求,伺服器會查詢名為lemontree7777777的使用者是否存在,最終伺服器返回true表示名為lemontree7777777的使用者已經存在了,瀏覽器在得到結果後顯示“使用者名稱已被註冊!”。
a.整個過程中頁面沒有重新整理,只是區域性重新整理了;
b.在請求發出後,瀏覽器不用等待伺服器響應結果就可以進行其他操作;
4.AJAX的優缺點
優點:
1.AJAX使用JavaScript技術向伺服器傳送非同步請求;
2.AJAX請求無須重新整理整個頁面;
3.因為伺服器響應內容不再是整個頁面,而是頁面中的部分內容,所以AJAX效能高;
5.作業
寫一個登陸認證頁面,登陸失敗不重新整理頁面,提示使用者登陸失敗,登陸成功自動跳轉到網站首頁。
login.html,內容如下:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
使用者名稱:<input type="text" id="username">
密碼:<input type="text" id="pwd">
{% csrf_token %}
<button id="sub">提交</button>
<span style="color: red;font-size: 12px;" id="error"></span>
</div>
<script src="{% static 'jquery.js' %}"></script>
<script>
$('#sub').click(function () {
$.ajax({
url:"{% url 'login' %}",
type:'post',
data:{username:$('#username').val(),pwd:$('#pwd').val(),csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val()},
success:function (data) {
data = JSON.parse(data);
console.log(data,typeof data);
if (data['status']){
location.href=data['home_url'];
}
else {
$('#error').text('使用者名稱或者密碼錯誤!')
}
}
})
})
</script>
</body>
</html>
base.html,內容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
歡迎來到xxx官網
</h1>
</body>
</html>
urls.py,內容如下
url(r'^login/', views.login,name='login'),
url(r'^home/', views.home,name='home'),
views.py,內容如下
def login(request):
res_dict = {'status':None,'home_url':None}
if request.method == 'GET':
return render(request,'login.html')
else:
uname = request.POST.get('username')
pwd = request.POST.get('pwd')
user_obj = models.UserInfo.objects.filter(name=uname,password=pwd).exists()
import json
if user_obj:
res_dict['status'] = True
res_dict['home_url'] = reverse('home')
res_json_dict = json.dumps(res_dict)
return HttpResponse(res_json_dict) #直接回復字典格式是不可以的,必須轉換成json字串,轉換成普通字串也是不行的,因為前端需要對json進行反序列獲得這個字典,在通過字典的形式來操作資料。
else:
res_dict['status'] = False
res_json_dict = json.dumps(res_dict)
return HttpResponse(res_json_dict)
# 如果你就是不使用JsonResponse的話,也可以給HttpResponse新增一個引數,content_type='application/json',那麼前端ajax拿到資料之後,也是不需要反序列化的,ajax的回撥函式就收到的就是一個反序列化之後的一個物件,因為ajax接受到資料後,通過這個data_type或者content_type發現你傳送來的是個json格式的資料,那麼ajax內容就自動將這個資料反序列化得到了js的資料物件,然後通過物件可以直接操作資料。 # return HttpResponse(res_json_dict,data_type='application/json') # return JsonResponse(res_dict)
def home(request):
return render(request,'base.html')
還有一點注意一下,如果你想通過ajax來刪除表格中某條記錄,並且ajax裡面的url不寫死的情況下(url反向解析),那麼就需要下面這種方式,實現url裡面引數的動態:
還有一個細節要注意:
並且刪除一條資料的時候,後端刪除成功之後,你通過後端給你的返回值判斷後端是否刪除成功,如果刪除成功,你有兩種方式來刪除前端頁面的對應一行的記錄,1:重新整理頁面,2:如果不讓重新整理頁面,那麼你就需要找到你點選的這個按鈕的那一行的tr標籤,通過dom操作把它刪除
ajax裡面寫$(this)時要注意的問題:還有一點注意,如果你新增某些dom物件的時候,如果你想在不重新整理頁面的情況下來新增這個物件,那麼你要注意,如果這個物件也需要繫結事件的話,你需要用on來給和他相同的標籤物件來繫結事件。
在這裡補充個事情:
settings配置檔案裡面加上下面這句話,意思是說,告訴django,如果別人請求我的路徑的時候,你不要自己處理別人輸入的路徑最後面的/了,如果這個值為True,而我們假如寫了一個url為url('^index/',views.test),如果使用者輸入的時127.0.0.1:8000/index的話,django會讓瀏覽器重新再發一次請求,並且在這個路徑後面加上/,也就成了127.0.0.1:8000/index/,此時和我們的url就能匹配上了,因為我們的url正則寫的就加了/,如果你將下面這個值設定成false,那麼django就不會自動幫你做這個事情了,那麼使用者在輸入127.0.0.1:8000/index,沒有最後那個斜槓的路徑時,就無法和我們的url正則匹配上了,所以就找不到url了,就會報錯,但是注意,django只能幫你重定向讓瀏覽器再發一個get請求,如果你是post請求(非get請求),django就沒有辦法了,他還是幫你重新定向傳送get請求,不能滿足你的需求,所以如果你用post方法提交資料的時候,就像上面這個ajax裡面的那個url寫的必須和你後端配置的那個url對應好,所以別忘了如果你後端url上url('^index/',views.test)這個index後面加了/,那麼你寫ajax往這個路徑下提交資料的時候,ajax裡面的url引數後面別忘了寫上/,讓這個url和後端url正則規則對應好。
二 Ajax的使用
1.基於jQuery的實現
看程式碼:
<button class="send_Ajax">send_Ajax</button>
<script>
$(".send_Ajax").click(function(){
$.ajax({
url:"/handle_Ajax/",
type:"POST",
data:{username:"chao",password:123},
success:function(data){
console.log(data)
},
error: function (jqXHR, textStatus, err) {
console.log(arguments);
},
complete: function (jqXHR, textStatus) {
console.log(textStatus);
},
statusCode: {
'403': function (jqXHR, textStatus, err) {
console.log(arguments);
},
'400': function (jqXHR, textStatus, err) {
console.log(arguments);
}
}
})
})
</script>
2.基於原生js實現
看程式碼
var b2 = document.getElementById("b2");
b2.onclick = function () {
// 原生JS
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", "/ajax_test/", true);
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHttp.send("username=chao&password=123456");
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
alert(xmlHttp.responseText);
}
};
};
3.Ajax-伺服器-Ajax流程圖
4.ajax引數
請求引數:
######################------------data---------################
data: 當前ajax請求要攜帶的資料,是一個json的object物件,ajax方法就會預設地把它編碼成某種格式
(urlencoded:?a=1&b=2)傳送給服務端;此外,ajax預設以get方式傳送請求。
function testData() {
$.ajax("/test",{ //此時的data是一個json形式的物件
data:{
a:1,
b:2
}
}); //?a=1&b=2
######################------------processData---------################
processData:聲明當前的data資料是否進行轉碼或預處理,預設為true,即預處理;if為false,
那麼對data:{a:1,b:2}會呼叫json物件的toString()方法,即{a:1,b:2}.toString()
,最後得到一個[object,Object]形式的結果。
######################------------contentType---------################
contentType:預設值: "application/x-www-form-urlencoded"。傳送資訊至伺服器時內容編碼型別。
用來指明當前請求的資料編碼格式;urlencoded:?a=1&b=2;如果想以其他方式提交資料,
比如contentType:"application/json",即向伺服器傳送一個json字串:
$.ajax("/ajax_get",{
data:JSON.stringify({
a:22,
b:33
}),
contentType:"application/json",
type:"POST",
}); //{a: 22, b: 33}
注意:contentType:"application/json"一旦設定,data必須是json字串,不能是json物件
views.py: json.loads(request.body.decode("utf8"))
######################------------traditional---------################
traditional:一般是我們的data資料有陣列時會用到 :data:{a:22,b:33,c:["x","y"]},
traditional為false會對資料進行深層次迭代;
響應引數:
dataType: 預期伺服器返回的資料型別,伺服器端返回的資料會根據這個值解析後,傳遞給回撥函式。
預設不需要顯性指定這個屬性,ajax會根據伺服器返回的content Type來進行轉換;
比如我們的伺服器響應的content Type為json格式,這時ajax方法就會對響應的內容
進行一個json格式的轉換,if轉換成功,我們在success的回撥函式裡就會得到一個json格式
的物件;轉換失敗就會觸發error這個回撥函式。如果我們明確地指定目標型別,就可以使用
data Type。
dataType的可用值:html|xml|json|text|script
見下dataType例項
三 Ajax請求設定csrf_token
方式1
通過獲取隱藏的input標籤中的csrfmiddlewaretoken值,放置在data中傳送。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "chao",
"password": 123456,
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
},
success: function (data) {
console.log(data);
}
})
方式2
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
方式3(後面再說)
通過獲取返回的cookie中的字串 放置在請求頭中傳送。
注意:需要引入一個jquery.cookie.js外掛。
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$.ajax({
headers:{"X-CSRFToken":$.cookie('csrftoken')}, #其實在ajax裡面還有一個引數是headers,自定製請求頭,可以將csrf_token加在這裡,我們發contenttype型別資料的時候,csrf_token就可以這樣加
})
詳述CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF。攻擊者通過HTTP請求江資料傳送到伺服器,從而盜取回話的cookie。盜取回話cookie之後,攻擊者不僅可以獲取使用者的資訊,還可以修改該cookie關聯的賬戶資訊。
所以解決csrf攻擊的最直接的辦法就是生成一個隨機的csrftoken值,儲存在使用者的頁面上,每次請求都帶著這個值過來完成校驗。
那麼django中csrf認證怎麼玩的呢?
官方文件中說到,檢驗token時,只比較secret是否和cookie中的secret值一樣,而不是比較整個token。
我又有疑問了,同一次登入,form表單中的token每次都會變,而cookie中的token不便,django把那個salt儲存在哪裡才能保證驗證通過呢。直到看到原始碼。
def _compare_salted_tokens(request_csrf_token, csrf_token):
# Assume both arguments are sanitized -- that is, strings of
# length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS.
return constant_time_compare(
_unsalt_cipher_token(request_csrf_token),
_unsalt_cipher_token(csrf_token),
)
def _unsalt_cipher_token(token):
"""
Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length
CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt
the second half to produce the original secret.
"""
salt = token[:CSRF_SECRET_LENGTH]
token = token[CSRF_SECRET_LENGTH:]
chars = CSRF_ALLOWED_CHARS
pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt))
secret = ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok
return secret
token字串的前32位是salt, 後面是加密後的token, 通過salt能解密出唯一的secret。
django會驗證表單中的token和cookie中token是否能解出同樣的secret,secret一樣則本次請求合法。
同樣也不難解釋,為什麼ajax請求時,需要從cookie中拿取token新增到請求頭中。
Cookies Hashing:每一個表單請求中都加入隨機的Cookie,由於網站中存在XSS漏洞而被偷竊的危險。
HTTP refer:可以對伺服器獲得的請求來路進行欺騙以使得他們看起來合法,這種方法不能夠有效防止攻擊。
驗證碼:使用者提交的每一個表單中使用一個隨機驗證碼,讓使用者在文字框中填寫圖片上的隨機字串,並且在提交表單後對其進行檢測。
令牌Token:一次性令牌在完成他們的工作後將被銷燬,比較安全。
...等等吧,還有很多其他的。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 從Cookie取csrftoken,並設定到請求頭中
data: {"username": "chao", "password": 123456},
success: function (data) {
console.log(data);
}
})
或者用自己寫一個getCookie方法:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
每一次都這麼寫太麻煩了,可以使用$.ajaxSetup()方法為ajax請求統一設定。
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
注意:
如果使用從cookie中取csrftoken的方式,需要確保cookie存在csrftoken值。
如果你的檢視渲染的HTML檔案中沒有包含 {% csrf_token %},Django可能不會設定CSRFtoken的cookie。
這個時候需要使用ensure_csrf_cookie()裝飾器強制設定Cookie。
django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def login(request):
pass
更多細節詳見:Djagno官方文件中關於CSRF的內容
四 Ajax檔案上傳
請求頭ContentType
ContentType指的是請求體的編碼型別,常見的型別共有3種:
1 application/x-www-form-urlencoded(看下圖)
這應該是最常見的 POST 提交資料的方式了。瀏覽器的原生 表單,如果不設定 enctype
屬性,那麼最終就會以 預設格式application/x-www-form-urlencoded 方式提交資料,ajax預設也是這個。請求類似於下面這樣(無關的請求頭在本文中都省略掉了):
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
user=yuan&age=22 #這就是上面這種contenttype規定的資料格式,後端對應這個格式來解析獲取資料,不管是get方法還是post方法,都是這樣拼接資料,大家公認的一種資料格式,但是如果你contenttype指定的是urlencoded型別,但是post請求體裡面的資料是下面那種json的格式,那麼就出錯了,服務端沒法解開資料。
看network來檢視我們傳送的請求體:
點選一下上面紅框的內容,你就會看到,這次post請求傳送資料的原始格式
2 multipart/form-data
這又是一個常見的 POST 資料提交的方式。我們使用表單上傳檔案時,必須讓 表單的 enctype
等於 multipart/form-data,form表單不支援發json型別的contenttype格式的資料,而ajax什麼格式都可以發,也是ajax應用廣泛的一個原因。直接來看一個請求示例:(瞭解)
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="user"
chao
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
這個例子稍微複雜點。首先生成了一個 boundary 用於分割不同的欄位,為了避免與正文內容重複,boundary 很長很複雜。然後 Content-Type 裡指明瞭資料是以 multipart/form-data 來編碼,本次請求的 boundary 是什麼內容。訊息主體裡按照欄位個數又分為多個結構類似的部分,每部分都是以 --boundary
開始,緊接著是內容描述資訊,然後是回車,最後是欄位具體內容(文字或二進位制)。如果傳輸的是檔案,還要包含檔名和檔案型別資訊。訊息主體最後以 --boundary--
標示結束。關於 multipart/form-data 的詳細定義,請前往 rfc1867 檢視。
這種方式一般用來上傳檔案,各大服務端語言對它也有著良好的支援。
上面提到的這兩種 POST 資料的方式,都是瀏覽器原生支援的,而且現階段標準中原生 表單也只支援這兩種方式(通過 元素的 enctype
屬性指定,預設為 application/x-www-form-urlencoded
。其實 enctype
還支援 text/plain
,不過用得非常少)。
隨著越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行資料互動之後,我們完全可以定義新的資料提交方式,給開發帶來更多便利。
3 application/json
application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端訊息主體是序列化後的 JSON 字串。由於 JSON 規範的流行,除了低版本 IE 之外的各大瀏覽器都原生支援 JSON.stringify,服務端語言也都有處理 JSON 的函式,使用 JSON 不會遇上什麼麻煩。
JSON 格式支援比鍵值對複雜得多的結構化資料,這一點也很有用。記得以前做過一個專案時,需要提交的資料層次非常深,我就是把資料 JSON 序列化之後來提交的。不過當時我是把 JSON 字串作為 val,仍然放在鍵值對裡,以 x-www-form-urlencoded 方式提交。
如果在ajax裡面寫上這個contenttype型別,那麼data引數對應的資料,就不能是個object型別資料了,必須是json字串,contenttype:'json',簡寫一個json,它也能識別是application/json型別
服務端接受到資料之後,通過contenttype型別的值來使用不同的方法解析資料,其實就是服務端框架已經寫好了針對這幾個型別的不同的解析資料的方法,通過contenttype值來找對應方法解析,如果有一天你寫了一個contenttype型別,定義了一個訊息格式,各大語言及框架都支援,那麼別人也會寫一個針對你的contenttype值來解析資料的方法,django裡面不能幫我們解析contenttype值為json的資料格式,你知道他能幫你解析application/x-www-form-urlencoded 和multipart/form-data(檔案上傳會用到)就行了,如果我們傳json型別的話,需要我們自己來寫一個解析資料的方法,其實不管是什麼型別,我們都可以通過原始傳送來的資料來進行加工處理,解析出自己想要的資料,這個事情我們在前面自己寫web框架的時候在獲取路徑那裡就玩過了,還記得嗎?
$.ajax({
url:"{% url 'home' %}",
type:'post',
headers:{
"X-CSRFToken":$.cookie('csrftoken'), #現在先記住,等學了cookies你就明白了
contentType:'json',
},
data:JSON.stringify({ //如果我們傳送的是json資料格式的資料,那麼csrf_token就不能直接寫在data裡面了,沒有效果,必須通過csrf的方式3的形式來寫,寫在hearders(請求頭,可以寫一些自定製的請求頭)裡面,注意,其實contentType也是headers裡面的一部分,寫在裡面外面都可以
name:name,
//csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
}),
success:function (response) {
}
})
基於form表單的檔案上傳
模板部分
<form action="" method="post" enctype="multipart/form-data"> #上面說的其他兩種contenttype都是鍵值的形式傳送資料,這種form_data的格式一般是把大資料一段一段隔開的
使用者名稱 <input type="text" name="user">
頭像 <input type="file" name="avatar"> #如果不用form_data格式來發,那麼預設的是urlencoded的格式,這個標籤的資料會組成avatar:檔名字來進行傳送
<input type="submit">
</form>
檢視部分
def index(request):
print(request.body) # 原始的請求體資料
print(request.GET) # GET請求資料
print(request.POST) # POST請求資料
print(request.FILES) # 上傳的檔案資料
return render(request,"index.html")
upload.py,內容如下:
def upload(request):
if request.method == 'GET':
return render(request,'upload.html')
else:
print(request.POST)
username = request.POST.get('user')
file_obj = request.FILES.get('file_obj') #獲得檔案資料物件
print('>>>',file_obj,type(file_obj))
#>>> jaden部落格.txt <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>,一個檔案物件,可以理解為一個檔案控制代碼
file_name = file_obj.name #jaden部落格.txt
print(file_name)
# 將資料寫到檔案裡面,需要名字,需要資料
with open(file_name,'wb') as f: #直接把檔名字放這裡,那麼檔案將直接生成在django的整個專案目錄下,因為django配置的系統搜尋的根路徑就是咱們的專案資料夾路徑,那個BASE_DIR,一般我們需要自己建立一個資料夾專門存放上傳的檔案 #所以需要我們自己來拼接一個路徑放到這裡,os.path.join(settings.BASE_DIR,'media','img',file_name)
# f.write() #不能一下寫進去,佔用的內容太多,要一點一點寫
for data in file_obj: #讀資料
f.write(data) #每次讀取的data不是固定長度的,和讀取其他檔案一樣,每次讀一行,識別符為\r \n \r\n,遇到這幾個符號就算是讀了一行
#for chunks in file_obj.chunks(): #chunks()預設一次返回大小為經測試為65536B,也就是64KB,最大為2.5M,是一個生成器 # f.write(chunks)
通過js來找檔案物件
基於Ajax的檔案上傳
模板
<form> #用不用form沒關係,這裡就是個盒子的作用,一般寫form標籤是為了提示別人,這個地方的內容是要提交的
{% csrf_token %} 使用者名稱 <input type="text" id="user">
頭像 <input type="file" id="avatar">
<input type="button" id="ajax-submit" value="ajax-submit">
</form>
<script>
$("#ajax-submit").click(function(){
var formdata=new FormData(); #ajax上傳檔案的時候,需要這個型別,它會將新增給它的鍵值對加工成formdata的型別
formdata.append("user",$("#user").val()); #新增鍵值的方法是append,注意寫法,鍵和值之間是逗號
formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); #別忘了csrf_token
formdata.append("avatar_img",$("#avatar")[0].files[0]);
$.ajax({
url:"",
type:"post",
data:formdata, #將新增好資料的formdata放到data這裡
processData: false , // 不處理資料
contentType: false, // 不設定內容型別
success:function(data){
console.log(data)
}
})
})
</script>
或者使用
var form = document.getElementById("form1");
var fd = new FormData(form);
這樣也可以直接通過ajax 的 send() 方法將 fd 傳送到後臺。
注意:由於 FormData 是 XMLHttpRequest Level 2 新增的介面,現在 低於IE10 的IE瀏覽器不支援 FormData。
檢視
def index(request):
if request.is_ajax():
print(request.body) # 原始的請求體資料
print(request.GET) # GET請求資料
print(request.POST) # POST請求資料
print(request.FILES) # 上傳的檔案資料
return HttpResponse("ok")
return render(request,"index.html")
檢查瀏覽器的請求頭:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaWl9k5ZMiTAzx3FT
關於django後端程式碼接受上傳檔案的方法
當Django處理上傳一個檔案的時候,檔案資料被放在request.FILES中。這個文件解釋檔案怎麼樣被儲存在磁碟上或者記憶體中,怎樣定製預設的行為。
基本檔案上傳
考慮一個包含FileField的簡單的表單:
from django import forms
classUploadFileForm(forms.Form):
title=forms.CharField(max_length=50)
file=forms.FileField()
一個處理這個表單的檢視將在request.FILES中接受檔案資料 ,request.FILES是一個字典,它對每個FileField(或者是ImageField,或者是其他的FileField的子類)都包含一個key.所以 從上面的表單中來的資料將可以通過request.FILES['file']鍵來訪問.
注意request.FILES只有 在request方法是POST並且發出POST請求的
有屬性enctype="multipart/form-data".否則,request。FILES將是空的。
看另一個簡單的;
from fdjango.http improt HttpResponseRedirect
from django.shortcuts import render_to_response
from somewhere import handle_uploaded_file
def upload_file(request):
if request.method == 'post':
form = UploadFileForm(rquest.POST,request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
return HttpResponseRedirect('/success/ur/')
else:
form = UploadFileForm()
return render_to_response('upload.html',{'form':form})
要注意,我們必須將request.FILES傳遞到表單的構造器中;這就是檔案資料怎樣和表單沾上邊的 。
處理上傳的檔案
最後的難題是怎樣處理從request.FILES中獲得的真實的檔案。這個字典的每個輸入都是一個UploadedFile物件——一個上傳之後的檔案的簡單的包裝。
你通常會使用下面的幾個方法來訪問被上傳的內容:
UploadedFile.read():從檔案中讀取整個上傳的資料。小心整個方法:如果這個檔案很大,你把它讀到記憶體中會弄慢你的系統。你可以想要使用chunks()來代替,看下面;
UploadedFile.multiple_chunks():如果上傳的檔案足夠大需要分塊就返回真。預設的這個值是2.5兆,當然這個值是可以調節的,看下面的UploadedFile.chunks():一個產生器,返回檔案的塊。如果multiple_chunks()是真的話,你應該在一個迴圈中使用這個方法,而不是使用read();
UploadedFile.name:上傳檔案的名字(比如m_file.txt)
UploadedFile.size:以bytes表示的上傳的檔案的大小。
還有其他的幾個方法和屬性。你可以自己去查。
把他們放在一起,這裡是一個你處理上傳檔案的通常方法:
def handle_uploaded_file(f):
destination = open('some/file/name.txt','wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
在UploadedFile.chunks()上迴圈而不是用read()保證大檔案不會大量使用你的系統記憶體。
上傳的資料存在哪裡?
在你儲存上傳的檔案之前,資料需要被儲存在某些地方。預設呢的,如果一個上傳的檔案小於2.5兆,Django會將上傳的東西放在記憶體裡。這意味著只要從記憶體讀取資料並儲存到硬碟上,所以很快。然而,如果一個上傳的檔案太大,Django將將上傳的檔案寫到一個臨時的檔案中,這個檔案在你的臨時檔案路徑中。在Unix-like的平臺上意味著你可以預見Django產生一個檔案儲存為/tmp/tmpzfp6I6.upload的檔案。如果這個檔案足夠大,你可以觀察到這個檔案的大小在增大。
很多細節--2.5M;/tmp;等 等 都是簡單的看上去合理的預設值。繼續閱讀看看你怎麼樣個性化或者完全替代掉上傳行為。
改變上傳處理行為
三個設定改變Django的上傳處理行為:
FILE_UPLOAD_MAX_MEMORY_SIZE:以bytes為單位的到記憶體中的最大大小,。比這個值大的檔案將被先存到磁碟上。預設是2.5兆
FILE_UPLOAD_TEMP_DIR:比FILE_UPLOAD_MAX_MEMORY_SIZE大的檔案將被臨時儲存的地方。預設是系統標準的臨時路徑。
FILE_UPLOAD_PERMISSIONS:如果這個沒有給出或者是None,你將獲得獨立於系統的行為。大多數平臺,臨時檔案有一個0600模式,從記憶體儲存的檔案將使用系統標準umask。
FILE_UPLOAD_HANDLERS:上傳檔案的處理器。改變這個設定允許完全個性化——甚至代替——Django的上傳過程。
預設是:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
UploadedFile 物件
class UploadedFile
作為那些重File繼承的補充,素有的UploadedFile物件定義了下面的方法和屬性:
UploadedFile.content_type
檔案的content_type頭(比如text/plain
orapplication/pdf
)。像使用者提供的任何資料一樣,你不應該信任上傳的資料就是這個型別。你仍然要驗證這個檔案包含這個頭宣告的content-type——“信任但是驗證”。
UploadedFile.charset
對於text/*的content-types,瀏覽器提供的字符集。再次,“信任但是驗證”是最好的策略。
UploadedFile.temporary_file_path():只有被傳到磁碟上的檔案才有這個方法,它返回臨時上傳檔案的全路徑。
注意:
像通常的Python檔案,你可以迭代上傳的檔案來一行一行得讀取檔案:
for line in uploadedfile:
do_something_with(line)
然而,不同於標準Python檔案,UploadedFile值懂得/n(也被稱為Unix風格)的結尾。如果你知道你需要處理有不同風格結尾的檔案的時候,你要在你的檢視中作出處理。
上傳處理控制代碼:
當一個使用者上傳一個檔案,Django敬愛那個這個檔案資料傳遞給上傳處理控制代碼——一個處理隨著檔案上傳處理檔案的小類。上傳處理控制代碼被FILE_UPLOAD_HANDLERS初始化定義,預設是:
(
"django.core.files.uploadhandler.MemoryFileUploadHandler"
,
"django.core.files.uploadhandler.TemporaryFileUploadHandler"
,)
這兩個提供了Django處理小檔案和大檔案的預設上產行為。
你可以個性化處理控制代碼來個性化Django處理檔案的行為。比如你可以使用個性化的處理控制代碼來強制使用者配額,實時地壓縮資料,渲染進度條,甚至在儲存在本地的同時向另一個儲存地傳送資料。
實時修改上傳處理控制代碼
有的時候某些檢視要使用不同的上傳行為。這種情況下,你可以重寫一個上傳處理控制代碼,通過request.upload_handlers來修改。預設的,這個列表包含FILE_UPLOAD_HANDLERS提供的處理控制代碼,但是你可以像修改其他列表一樣修改這個列表。
比如,加入你寫了一個叫做
ProgressBarUploadHandler
的處理控制代碼。你可以通過下面的形式加到你的上傳處理控制代碼中:
request.upload_handlers.insert(0,ProgressBarUploadHandler())
你贏使用list.insert()在這種情況下。因為進度條處理控制代碼需要首先執行。記住,處理控制代碼按照順序執行。
如果你像完全代替掉上傳處理控制代碼,你可以賦值一個新的列表:
request.upload_handlers=[ProgressBarUploadHandler()]
注意:你只能在訪問request.POST或者request.FILES之前修改上傳處理控制代碼。——如果上傳處理開始後再改就沒用了。如果你在修改reqeust.uplaod_handlers之前訪問了request.POST
or request.FILES
,Django將丟擲一個錯誤。
所以,在你的檢視中儘早的修改上傳處理控制代碼。
寫自定義的上傳處理控制代碼:
所有的上傳處理控制代碼都應 是 django.core.files.uploadhandler.FileUploadHandler的子類。你可以在任何你需要的地方定義控制代碼。
需要的方法:
自定義的上傳處理控制代碼必須定義一下的方法:
FileUploadHandler.receive_data_chunk(self,raw_data,start):從檔案上傳中接收塊。
raw_data是已經上傳的位元組流
start是raw_data塊開始的位置
你返回的資料將被傳遞到下一個處理控制代碼的receive_data_chunk方法中。這樣一個處理控制代碼就是另一個的過濾器了。
返回None將阻止後面的處理控制代碼獲得這個塊,當你 自己儲存這個資料,而不想其他處理控制代碼儲存拷貝時很有用。
如果你觸發一個StopUpload或者SkipFile異常,上傳將被放棄或者檔案被完全跳過。
FileUploadHandler.file_complete(self, file_size)
當 檔案上傳完畢時呼叫。
處理控制代碼應該返回一個UploadFile物件,可以儲存在request.FILES中。處理控制代碼也可以返回None來使得UploadFile物件應該來自後來的上傳處理控制代碼。
剩下的就是可選的一些方法實現。
FILE_UPLOAD_MAX_MEMORY_SIZE = 209715200
FILE_UPLOAD_MAX_MEMORY_SIZE = 209715200
在你本機先好好測試一下,它是如何佔用記憶體,什麼時候開始存入temp目錄,怎麼遷移到upload目錄底下的
檔案上傳的時候,如果一個上傳的檔案小於2.5兆,Django會將上傳的東西放在記憶體裡,如果上傳的檔案大於2.5M,Django將整個上傳的檔案寫到一個臨時的檔案中,這個檔案在臨時檔案路徑中。上傳完畢後,將呼叫View中的_Upload()方法將臨時資料夾中的臨時檔案分塊寫到上傳檔案的存放路徑下,每塊的大小為64K,寫完後臨時檔案將被刪除。
UploadedFile.multiple_chunks():如果上傳的檔案足夠大需要分塊就返回真。預設的這個值是2.5兆,當然這個值是可以調節的,看下面的UploadedFile.chunks():一個產生器,返回檔案的塊。如果multiple_chunks()是真的話,你應該在一個迴圈中使用這個方法,而不是使用read();
在你儲存上傳的檔案之前,資料需要被儲存在某些地方。預設呢的,如果一個上傳的檔案小於2.5兆,Django會將上傳的東西放在記憶體裡。這意味著只要從記憶體讀取資料並儲存到硬碟上,所以很快。然而,如果一個上傳的檔案太大,Django將上傳的檔案寫到一個臨時的檔案中,這個檔案在你的臨時檔案路徑中。在Unix-like的平臺上意味著你可以預見Django產生一個檔案儲存為/tmp/tmpzfp6I6.upload的檔案。如果這個檔案足夠大,你可以觀察到這個檔案的大小在增大。
三個設定改變Django的上傳處理行為:
FILE_UPLOAD_MAX_MEMORY_SIZE:以bytes為單位的到記憶體中的最大大小,。比這個值大的檔案將被先存到磁碟上。預設是2.5兆
FILE_UPLOAD_TEMP_DIR:比FILE_UPLOAD_MAX_MEMORY_SIZE大的檔案將被臨時儲存的地方。預設是系統標準的臨時路徑。
FILE_UPLOAD_PERMISSIONS:如果這個沒有給出或者是None,你將獲得獨立於系統的行為。大多數平臺,臨時檔案有一個0600模式,從記憶體儲存的檔案將使用系統標準umask。
練習(使用者名稱是否已被註冊)
功能介紹
在登錄檔單中,當用戶填寫了使用者名稱後,把游標移開後,會自動向伺服器傳送非同步請求。伺服器返回這個使用者名稱是否已經被註冊過。
案例分析
- 頁面中給出登錄檔單;
- 在username input標籤中繫結onblur事件處理函式。
- 當input標籤失去焦點後獲取 username表單欄位的值,向服務端傳送AJAX請求;
- django的檢視函式中處理該請求,獲取username值,判斷該使用者在資料庫中是否被註冊,如果被註冊了就返回“該使用者已被註冊”,否則響應“該使用者名稱可以註冊”。
def index(request):
if request.method == 'POST':
print(request.POST)
print('files',request.FILES)
# book_objs = models.Book.objects.all()
# ret = serializers.serialize('json', book_objs,cls=JsonCustomEncoder) #同樣無法序列化時間
# ret = serializers.serialize('json', book_objs,cls=JsonCustomEncoder) #同樣無法序列化時間,無法和json一樣引入cls=JsonCustomEncoder這個類來解決日期時間格式資料的序列化
#得到的結果[{"model": "app01.book", "pk": 1, "fields": {"name": "python", "date": null, "img": ""}}, {"model": "app01.book", "pk": 2, "fields": {"name": "linux", "date": null, "img": ""}}]
#得到的結果的格式也是比較繁瑣,所以不推薦使用django自帶的serializers序列化器來序列化我們的queryset型別的資料
#推薦寫法:
ret = models.Book.objects.all().values()
print(ret)
# print(request.body)
import json
ret = json.dumps(list(ret),cls=JsonCustomEncoder)
print(ret)
print(type(ret))
# return HttpResponse(ret,content_type='application\json')
return JsonResponse(ret,safe=False)
五 關於json
1.什麼是json
- JSON 指的是 JavaScript 物件表示法(JavaScript Object Notation)
- JSON 是輕量級的文字資料交換格式
- JSON 獨立於語言 *
JSON 具有自我描述性,更易理解
JSON 使用 JavaScript 語法來描述資料物件,但是 JSON 仍然獨立於語言和平臺。JSON 解析器和 JSON 庫支援許多不同的程式語言。
啥都別多說了,上圖吧!
json資料型別和python資料型別的對比:
object和python的dict型別是差不多的,但是要求裡面必須是雙引號,string和list、tuple等也是一樣的,都是雙引號。python中的datetime等時間日期型別是不能進行json序列化的,因為json沒有對應的格式,上面的這幾種資料型別雖然進行json.dumps序列化之後都是個字串,但是也是有格式的
前端ajax拿到後端返回的一個python的json模組序列化之後的一個json字串,那麼js通過自己的json介面,將接受到的json字串來反序列化為js自己語言能夠識別的資料型別,然後再進行操作。
相當於我有一個json方法,你有一個json方法,你給我發資料必須是json字串的格式,那麼你就需要將你的資料型別序列化為json的字串,那麼序列化的時候,就把你的資料序列化為了符合json標準的字串,然後我接收到這個字串之後,我通過我的json方法,將資料轉換為我的語言支援的資料型別。在進行反序列化的時候,如果你的字串不符合json的格式,那麼反序列化的時候就會報錯,所以只要你是通過json序列化成的字串,都是能夠json反序列化的,因為json序列化的時候,就把你的資料改為了符合json標準的字串形式,例如:裡面的單引號,序列化後變成了雙引號。
合格的json物件:
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 } #這就是一個json的object型別,符合json的標準格式,就可以通過dumps來進行序列化
{"names": ["張三", "李四"] }
[ { "name": "張三"}, {"name": "李四"} ]
不合格的json物件:
{ name: "張三", 'age': 32 } // 屬性名必須使用雙引號
[32, 64, 128, 0xFFF] // 不能使用十六進位制值
{ "name": "張三", "age": undefined } // 不能使用undefined
{ "name": "張三",
"birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
"getName": function() {return this.name;} // 不能使用函式和日期物件
}
看一下普通字串和json字串,在進行序列化的時候的區別
import json
# s = "{'name':'chao','age':18}" #普通字串,每加引號的沒問題,加了引號的,必須是雙引號才能使用json.loads()。
s = '{"name":"chao","age":18}' #json字串,裡面必須是雙引號
ret = json.loads(s)
print(ret)
print(ret['name'])
2.js的stringify與parse方法
JavaScript中關於JSON物件和字串轉換的兩個方法:
JSON.parse(): 用於將一個 JSON 字串轉換為 JavaScript 物件
JSON.parse('{"name":"chao"}');
JSON.parse('{name:"chao"}') ; // 錯誤
JSON.parse('[18,undefined]') ; // 錯誤
JSON.stringify(): 用於將 JavaScript 值轉換為 JSON 字串。
JSON.stringify({"name":"chao"})
3.和XML的比較
JSON 格式於2001年由 Douglas Crockford 提出,目的就是取代繁瑣笨重的 XML 格式。
JSON 格式有兩個顯著的優點:書寫簡單,一目瞭然;符合 JavaScript 原生語法,可以由解釋引擎直接處理,不用另外新增解析程式碼。所以,JSON迅速被接受,已經成為各大網站交換資料的標準格式,並被寫入ECMAScript 5,成為標準的一部分。
XML和JSON都使用結構化方法來標記資料,下面來做一個簡單的比較。
用XML表示中國部分省市資料如下:
<?xml version="1.0" encoding="utf-8"?>
<country>
<name>中國</name>
<province>
<name>黑龍江</name>
<cities>
<city>哈爾濱</city>
<city>大慶</city>
</cities>
</province>
<province>
<name>廣東</name>
<cities>
<city>廣州</city>
<city>深圳</city>
<city>珠海</city>
</cities>
</province>
<province>
<name>臺灣</name>
<cities>
<city>臺北</city>
<city>高雄</city>
</cities>
</province>
<province>
<name>新疆</name>
<cities>
<city>烏魯木齊</city>
</cities>
</province>
</country>
用JSON表示如下:
{
"name": "中國",
"province": [{
"name": "黑龍江",
"cities": {
"city": ["哈爾濱", "大慶"]
}
}, {
"name": "廣東",
"cities": {
"city": ["廣州", "深圳", "珠海"]
}
}, {
"name": "臺灣",
"cities": {
"city": ["臺北", "高雄"]
}
}, {
"name": "新疆",
"cities": {
"city": ["烏魯木齊"]
}
}]
}
由上面的兩端程式碼可以看出,JSON 簡單的語法格式和清晰的層次結構明顯要比 XML 容易閱讀,並且在資料交換方面,由於 JSON 所使用的字元要比 XML 少得多,可以大大得節約傳輸資料所佔用得頻寬。
4.ajax和服務端的資料互動時的序列化問題
當我們給ajax回覆的不是一個字串,而是其他資料型別的時候,需要我們將資料轉換為json字串進行傳送,這樣好配合js進行json字串的處理,不然傳送或者接受的是普通字串的話,沒辦法處理成原來的資料型別。
這就用到了我們前面的檢視函式中那個JsonResponse了,看部落格,裡面response的部分
還要注意ajax中的data引數:
data引數中的鍵值對,如果值值不為字串,需要將其轉換成字串型別。
$("#b1").on("click", function () {
$.ajax({
url:"/ajax_add/",
type:"GET",
data:{"i1":$("#i1").val(),"i2":$("#i2").val(),"hehe": JSON.stringify([1, 2, 3])},
success:function (data) {
$("#i3").val(data);
}
})
})
5.Django內建的serializers做序列化
看程式碼:
def books_json(request):
book_list = models.Book.objects.all()[0:10]
from django.core import serializers
ret = serializers.serialize("json", book_list)
return HttpResponse(ret)
通過json學列化時間日期格式資料的時候需要注意,不能直接序列化,我寫了一個類,可以借用:
import json
from datetime import datetime
from datetime import date
#對含有日期格式資料的json資料進行轉換
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field,datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field,date):
return field.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self,field)
d1 = datetime.now()
dd = json.dumps(d1,cls=JsonCustomEncoder)
print(dd)
六 補充一個SweetAlert外掛示例
看效果:
點選下載Bootstrap-sweetalert專案。
$(".btn-danger").on("click", function () {
swal({
title: "你確定要刪除嗎?",
text: "刪除可就找不回來了哦!",
type: "warning",
showCancelButton: true,
confirmButtonClass: "btn-danger",
confirmButtonText: "刪除",
cancelButtonText: "取消",
closeOnConfirm: false
},
function () {
var deleteId = $(this).parent().parent().attr("data_id");
$.ajax({
url: "/delete_book/",
type: "post",
data: {"id": deleteId},
success: function (data) {
if (data.status === 1) {
swal("刪除成功!", "你可以準備跑路了!", "success");
} else {
swal("刪除失敗", "你可以再嘗試一下!", "error")
}
}
})
});
})
專案中簡單應用:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.0-dist/dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'bootstrap-sweetalert-master/dist/sweetalert.css' %}">
</head>
<body>
<div>
<button class="btn btn-danger">刪除</button>
</div>
</body>
<script src="{% static 'bootstrap-3.3.0-dist/dist/jQuery/jquery-3.1.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.0-dist/dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'bootstrap-sweetalert-master/dist/sweetalert.js' %}"></script>
<script>
$(".btn-danger").on("click", function () {
swal({
title: "你確定要刪除嗎?",
text: "刪除可就找不回來了哦!",
type: "warning",
showCancelButton: true,
confirmButtonClass: "btn-danger",
confirmButtonText: "我已經下定決心",
cancelButtonText: "容我三思",
closeOnConfirm: false
},
function () {
var deleteId = $(this).parent().parent().attr("data_id");
$.ajax({
url: "/delete_book/",
type: "post",
data: {"id": deleteId},
success: function (data) {
console.log(data,typeof data);
if (data === '1') {
swal("刪除成功!", "你可以準備跑路了!", "success");
} else {
swal("刪除失敗", "你可以再嘗試一下!", "error")
}
}
})
});
})
</script>
</html>
urls.py
url(r'^delete_book/', views.delete_book,name='delete_book'),
views.py
def delete_book(request):
import random
ret = str(random.randint(1,2))
return HttpResponse(ret)
七 同源策略與Jsonp
同源策略
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。
同源策略,它是由Netscape提出的一個著名的安全策略。現在所有支援JavaScript 的瀏覽器都會使用這個策略。所謂同源是指,域名,協議,埠相同。當一個瀏覽器的兩個tab頁中分別開啟來 百度和谷歌的頁面當瀏覽器的百度tab頁執行一個指令碼的時候會檢查這個指令碼是屬於哪個頁面的,即檢查是否同源,只有和百度同源的指令碼才會被執行。如果非同源,那麼在請求資料時,瀏覽器會在控制檯中報一個異常,提示拒絕訪問。
示例專案1:
==================================http://127.0.0.1:8001專案的index
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<button>ajax</button>
{% csrf_token %}
<script>
$("button").click(function(){
$.ajax({
url:"http://127.0.0.1:7766/SendAjax/",
type:"POST",
data:{"username":"yuan&