AJAX簡介
AJAX(Asynchronous Javascript And XML)翻譯成中文就是「異步Javascript和XML」. 即便用Javascript語言與服務器進行異步交互,傳輸的數據爲XML(固然,傳輸的數據不僅是XML,如今更多使用json數據).javascript
AJAX的特色:css
異步交互: 客戶端發出一個請求後,無需等待服務器響應結束,就能夠發出第二個請求html
這裏穿插異步同步的概念: 同步: 線程提交任務後, 必須原地等待任務結果產生, 才能繼續執行下一行代碼 異步: 線程提交任務後, 無需等待結果的產生, 直接執行下一行代碼. 當結果產生後, 會觸發回調函數來處理結果.
局部刷新: 使用ajax與服務器進行數據交換後, 會經過js代碼進行dom操做進行局部的頁面替換, 不會刷新整個頁面. 所以ajax的性能也比較高.java
JS實現AJAX
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% csrf_token %} <button id="id_js_button">原生js發送ajax請求</button> </body> <script> var b2 = document.getElementById("id_js_button"); b2.onclick = function () { // 原生JS var xmlHttp = new XMLHttpRequest(); xmlHttp.open("post", "/test_ajax/", true); xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); var csrf_token = document.getElementsByTagName('input')[0].value; console.log(csrf_token); xmlHttp.send("username=test&password=123456&csrfmiddlewaretoken=" + csrf_token); xmlHttp.onreadystatechange = function () { if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { alert(xmlHttp.responseText); } }; }; </script> </html>
因爲原生js發送ajax請求步驟太過麻煩與複雜, 下面發送的ajax請求都是基於jQuery發送的.python
JQuery實現AJAX
基本的實現流程:jquery
contentType: urlencoded
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> {% csrf_token %} <button id="id_jQuery_button">jQuery發送ajax請求</button> </body> <script> $('#id_jQuery_button').click(function () { $.ajax({ // url後面的參數表示須要提交的地址, 不寫默認給當前地址提交 url: '', // type表示提交的方式, get, post, application/json type: 'post', // data後面的參數是表示須要上傳的數據 data: { 'username': 'root', 'password': '123', 'csrfmiddlewaretoken': '{{csrf_token}}', }, // success表示服務端給出響應後執行的回調函數 // 數據傳遞給data接收 success: function (data) { console.log(data); } }) }) </script> </html
上面雖然是基於ajax發送post請求的, 可是它的默認編碼方式仍是urlencoded, 能夠從瀏覽器的請求中看出ajax
下面將利用json來進行數據的交互django
contentType: application/json
<script> $('#id_jQuery_button').click(function () { var send_data = { 'username': 'root', 'password': '123', }; $.ajax({ // url後面的參數表示須要提交的地址, 不寫默認給當前地址提交 url: '', // type表示提交的方式, get, post, application/json type: 'post', // 發送post請求, 須要注意這裏必須攜帶上這個參數, 不然會403錯誤 headers: { 'X-CSRFTOKEN': '{{csrf_token}}' }, //發送json數據格式的話, 須要改變contentType參數 contentType: 'application/json', // data後面的參數是表示須要上傳的數據 data: JSON.stringify(send_data), // success表示服務端給出響應後執行的回調函數 // 數據傳遞給data接收 success: function (data) { console.log(data); } }) }) </script>
使用json傳輸數據須要注意的是, 後端處理的過程也有點不同了. Django不會處理json數據, 而是把數據放在body裏面讓咱們本身來處理.json
def test_ajax(request): if request.is_ajax(): # 發送過來的若是是json字符串, Django不會幫咱們來處理 # 會將數據保存在body中, 須要咱們本身處理 print(request.body) import json print(json.loads(request.body, encoding='utf-8')) return JsonResponse({'code': 100, 'msg': '收到'}) return render(request, 'ajax_test.html') # out: # b'{"username":"root","password":"123"}' # {'username': 'root', 'password': '123'}
最後補充個json和Python數據類型相互轉換的結果.bootstrap
+---------------+-------------------+ | JSON | Python | +===============+===================+ | object | dict | +---------------+-------------------+ | array | list | +---------------+-------------------+ | string | str | +---------------+-------------------+ | number (int) | int | +---------------+-------------------+ | number (real) | float | +---------------+-------------------+ | true | True | +---------------+-------------------+ | false | False | +---------------+-------------------+ | null | None | +---------------+-------------------+
contentType: multipart/form-data;
最後一種是利用ajax發送了form-data格式的數據.
upload.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <title>Title</title> </head> <body> <div class="container"> <div class="row"> <div class="col-md-offset-2 col-md-8"> <h2>ajax上傳文件</h2> <hr> <form> {% csrf_token %} <div class="form-group"> <label for="InputFile">選擇圖片</label> <span> <label for="InputFile"> <img src="" alt="" width="200px" id="displayImg"> </label> <input type="file" id="InputFile" style="display: none"> </span> </div> <button type="button" class="btn btn-default" id="submit">提交</button> </form> </div> </div> </div> </body> <script> // 把選擇的圖片渲染到上面. let imgReader = new FileReader(); $('#InputFile').change(function () { // 從選擇的文件讀取內容 let selectFile = $('#InputFile')[0].files[0]; imgReader.readAsDataURL(selectFile); // 等待讀取徹底 imgReader.onload = function () { // 將圖片渲染到展現框 $('#displayImg').attr('src', imgReader.result); }; }); $('#submit').on('click', function () { // 這裏獲取文件內容並上傳 let formData = new FormData(); let selectFile = $('#InputFile')[0].files[0]; formData.append('file', selectFile); formData.append('csrfmiddlewaretoken', '{{ csrf_token }}'); $.ajax({ url: '', type: "post", data: formData, // ajax上傳文件必定要指定這兩個參數 contentType: false, processData: false, success: function (data) { if (data.code === 100) { alert('文件上傳成功!'); } else { alert('上傳失敗!') } } }) }) </script> </html>
view.py
def upload(request): if request.method == 'POST' and request.is_ajax(): # 獲取文件信息 print(request.FILES) # 讀取文件保存到本地中 file = request.FILES.get('file') with open(file.name, 'wb') as f: for chunk in file.chunks(): f.write(chunk) return JsonResponse({'code': 100}) return render(request, 'upload.html', locals())
上傳文件使用FormData傳輸, Content-Type自動指定爲form-data.
傳輸csrf_token的其餘方式
上面幾個例子, 都看出來了每次遇到post請求, 都須要咱們手動傳csrf_token
參數, 這樣很是麻煩, 所以咱們能夠更改jQuery的ajax傳遞方式, 在傳輸數據以前, 加入咱們須要的csrf_token
就能夠了. 詳細的信息參考Django官方文檔
方式1: 從cookie中獲取
注意:須要引入一個jquery.cookie.js插件。
<script> $.ajax({ url: "/cookie_ajax/", type: "POST", headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 從Cookie取csrf_token,並設置ajax請求頭 data: {"username": "test", "password": 123456}, success: function (data) { console.log(data); } }) </script>
方式2: 本身寫一個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');
方式3: 直接使用$.ajaxSetup()方法爲ajax請求統一設置, 即在發送前設置csrftoken參數.
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); } } });
注意: 上面這幾種方式都是基於jQuery上解決的, 須要先引入jQuery文件, 才能生效.
小練習
寫一個註冊頁面, 可以讓用戶將光標移開後可以顯示用戶名是否已被註冊過, 將光標從新聚焦輸入, 又會將錯誤信息清空.
註冊頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <title>Title</title> </head> <body> <div class="container"> <div class="row"> <div class="col-md-offset-3 col-md-6"> <h2>註冊頁面</h2> {% csrf_token %} <div class="form-group"> <label for="id_username">用戶名</label> <input type="text" class="form-control" name="username" id="id_username"> <span class="pull-right"></span> </div> <div class="form-group"> <label for="id_password">密碼</label> <input type="text" class="form-control" name="password" id="id_password"> <span class="pull-right"></span> </div> <div class="form-group"> <input type="button" class="btn btn-success" id="id_submit" value="提交"> </div> </div> </div> </div> <script> $('#id_username').blur(function () { // 判斷當用戶用戶名框移出, 就向服務器發送請求 let username = $(this).val(); let $username = $(this); $.ajax({ url: '', type: 'post', data: {'username': username, 'csrfmiddlewaretoken': '{{ csrf_token }}'}, success: function (data) { if (data.code === 100){ $username.next().css('color', 'black').text('用戶名可使用').parent().addClass('has-success'); } else if (data.code === 101) { $username.next().text('用戶名不能爲空').css('color', 'red').parent().addClass('has-error'); } else{ $username.next().text('用戶名已存在').css('color', 'red').parent().addClass('has-error') } } }) }).focus(function () { $(this).next().text('').parent().removeClass('has-error'); }); </script> </body> </html>
views.py
def reg(request): if request.method == 'POST': username = request.POST.get('username') back_dic = {'code': 100, 'msg': ''} if username in ['a', 'b', 'c']: back_dic['code'] = 102 back_dic['msg'] = '用戶名已存在' elif not username: back_dic['code'] = 101 back_dic['msg'] = '用戶名不能爲空' else: back_dic['msg'] = '用戶名可使用' return JsonResponse(back_dic) return render(request, 'reg.html')