Ajax 實戰(一)

HammerZe發表於2022-03-07

Ajax 實戰(一)

img

簡介

AJAX(Asynchronous Javascript And XML)翻譯成中文就是“非同步Javascript和XML”。即使用Javascript語言與伺服器進行非同步互動,傳輸的資料為XML(當然,傳輸的資料不只是XML,現在更多使用json資料)

  • 同步互動:客戶端發出一個請求後,需要等待伺服器響應結束後,才能發出第二個請求;
  • 非同步互動:客戶端發出一個請求後,無需等待伺服器響應結束,就可以發出第二個請求;

優點

  • 非同步
  • 區域性重新整理:不需要重新整理整個頁面,節省了資源的消耗,給使用者的體驗極佳

常見應用場景
image

入門案例

'''
需求:
實現簡單的計算器,加法舉例
通過Ajax,實現前端輸入兩個數字,伺服器做加法,返回到前端頁面
'''
'''urls.py'''
path('', views.test_Ajax)
'''views.py'''
from django.shortcuts import render, HttpResponse

def test_Ajax(request):
    if request.method == 'POST':
        num1 = int(request.POST.get('num1')) # 獲取前端提交的資料
        num2 = int(request.POST.get('num2'))
        print(num1, num2)
        return HttpResponse(num1 + num2) # 返回給前端
    return render(request, 'sum.html')
'''sum.html'''
'''html結構'''
<div class="container">
    <div class="row">
        <h1 class="text-center">Ajax</h1>
        <div class="col-md-6 col-lg-offset-3 ">
            <input type="text" id="d1" class="active form-control">
            <p class="text-center">+</p>
            <input type="text" id="d2" class="sucess form-control">
            <p class="text-center">=</p>
            <input type="text" id="d3" class="info form-control">
            <br>
            <button id='btn' class="btn btn-success btn-block">calculate</button>
        </div>
    </div>
</div>
'''js實現'''
<script>
    {#繫結點選事件#}
    $('#btn').click(function () {
        {#獲取input元素內輸入的值#}
        var num1 = $('#d1').val()
        var num2 = $('#d2').val()

        $.ajax({
            url: '',  //ajax請求的地址
            method: 'post', //請求方式
            data: {num1: num1, num2: num2}, //攜帶引數
            success: function (data) {  //服務端成功返回會回撥,執行匿名函式
                console.log(data)
                $('#d3').val(data)
            }
        })
    })
</script>

image


注意

使用Ajax的時候,在檢視函式中使用request物件方法判斷什麼型別的請求,如果是Ajax,不管本質是post還是get等,寫request.is_ajax()即可

基於Ajax進行登入驗證

需求:

1.前端登入頁面

2.後端資料庫驗證前端提交的資訊

'''urls.py'''
path('login/', views.login),
'''views.py'''
from django.shortcuts import render,redirect,HttpResponse
from app01 import models
from django.http import JsonResponse
import json

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    # elif request.method=="POST":
    elif request.is_ajax():
        response = {'status':200,'msg':None}
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 資料庫校驗
        res = models.User.objects.filter(username=username,password=password).first()
        if res:
            # 登入成功
            # return redirect('http://www.baidu.com')  # ajax使用重定向出錯
            response['msg']='登入成功'
        else:
            response['msg']='登入失敗,使用者名稱或密碼錯誤'
            response['status']=404
        # return HttpResponse(json.dumps(response))
        return JsonResponse(response,safe=False,json_dumps_params={'ensure_ascii':False})
'''models.py'''
from django.db import models

class User(models.Model):
    name=models.CharField(max_length=32)
    password=models.CharField(max_length=32)
<body>
{#<form action="" method="post">#}
    <div class="container">
        <h1 class="text-center">Ajax登入認證</h1>
        <div class="row">
            <div class="col-md-8 col-lg-offset-2">
                <p>Username: <input type="text" class="form-control" name="username" id="user_id"></p>
                <p>Password: <input type="password" class="form-control" name="password" id="pwd_id"></p>
                <p><button type="submit" class="btn btn-block btn-primary">提交</button> <span class="error" style="color: tomato"></span></p>
            </div>
        </div>
    </div>
{#</form>#}
</body>
<script>
    $('.btn').click(function (){
        $.ajax(
            {
                url:'/login/',
                method:'post',
                data:{username:$('#user_id').val(),password:$('#pwd_id').val()}, //返回給後端的資料
                success:function (data){
                    console.log(data)
                    //data是物件型別
                    if(data.status == 200){
                      //登入成功,前端重定向
                        location.href='http://www.baidu.com'

                    }else{
                        //登入失敗
                        $('.error').html(data.msg)
                    }
                },
                error:function (data){
                    console.log(data)
                    alert('請求錯誤')
                }
            }
        )
    })
</script>

image

注意

  • 如果使用Ajax,form表單提交完資料會自己重新整理,所有在使用button元素提交的時候會刷兩次,可以講form元素去掉

  • 如果使用Ajax,form元素也可以不去掉,那麼就不能使用button元素,可以使用input元素,type=‘button’

  • 在Ajax中,如果使用json模組序列化資料,前端返回的是字串不是物件,響應頭中是text/html格式,需要自己在html頁面通過JSON.parse(data)反序列化,ajax接收到資料後需要自己轉成物件

  • 在Ajax中,如果使用JsonResponse模組序列化資料,返回的就是一個物件,響應頭中是application/json格式,不需要自己手動反序列化,ajax接收到資料後會自動轉成物件

  • 如果使用Ajax,能不能解析返回的資料,取決於響應的型別,如果是json型別,那麼就自動解析,不是需要自己手動解析

  • 如果使用了ajax,後端就不要返回rediret,render,HttpResponse,直接返回JsonResponse,因為返回json前端才能處理

  • 總結:後端返回資料,統一都用JsonResponse

HTTP請求編碼格式和報文

我們知道請求和響應都有資料格式,請求格式常用得比如:urlencoded、form-data、json····響應格式常用得有:text/html、json····

  • application/x-www-form-urlencoded:窗體資料被編碼為名稱/值對。(預設)。空格轉換為 “+” 加號,但不對特殊字元編碼。
  • multipart/form-data:窗體資料被編碼為一條訊息,頁上的每個控制元件對應訊息中的一個部分。
  • text/plain:窗體資料以純文字形式進行編碼,其中不含任何控制元件或格式字元。
  • JSON:以純文字形式進行編碼,其格式為JSON

現有HTML程式碼如下:用屬性enctype的值來區分Content-Type

<form action="http://localhost:8080" method="post" enctype="application/x-www-form-urlencoded">

    <input type="text" name="userName" value="zhan gsan"/>
    <input type="text" name="password" value="password"/>
    <input type="file" name="resultFile" />
    <input type="submit" value="submit"/>

</form>

當Content-Type為不同值時,報文結果分別為下:

Content-Type=application/x-www-form-urlencoded

瀏覽器用x-www-form-urlencoded的編碼方式把form資料轉換成一個字串(name1=value1&name2=value2…),然後把這個字串append到url後面,用?分割,載入這個新的url。 當action為post時候,瀏覽器把form資料封裝到http body中,然後傳送到server。

 POST / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 62
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

userName=zhan+gsan&password=password&resultFile=dddddddddd.vlx

Content-Type=multipart/form-data

瀏覽器會把整個表單以控制元件為單位分割,併為每個部分加上Content-Disposition(form-data或者file),name(控制元件name)等資訊,並加上分割符(boundary)。
此報文分割符為:boundary=—-WebKitFormBoundarys70zFPQBqcgHeMy9

POST / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 659
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarys70zFPQBqcgHeMy9
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

------WebKitFormBoundarys70zFPQBqcgHeMy9
Content-Disposition: form-data; name="userName"

zhan gsan
------WebKitFormBoundarys70zFPQBqcgHeMy9
Content-Disposition: form-data; name="password"

password
------WebKitFormBoundarys70zFPQBqcgHeMy9
Content-Disposition: form-data; name="resultFile"; filename="dddddddddd.vlx"
Content-Type: application/octet-stream

{"nodes":[{"name":"11111","image":"theme/gast/ren.png","entityId":"1000001"},{"name":"5555","image":"theme/gast/ren.png","entityId":"1000001"}],"edges":[{"source":"11111","target":"5555","relation":"ssss","count":"1","currentExtDepth":"1"}]}
------WebKitFormBoundarys70zFPQBqcgHeMy9--

Content-Type=text/plain

POST / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 66
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: text/plain
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

userName=zhan gsan
password=password
resultFile=dddddddddd.vlx

參考:請求頭編碼格式

總結

1 http --post--請求,有編碼格式,主流有三種
	-urlencoded :預設的----》從request.POST取提交的資料
    -form-data :上傳檔案的----》從request.POST取提交的資料,request.FILES中取檔案
    -json      :ajax傳送json格式資料-----》request.POST取不出資料了,需要request.body
    
2 使用ajax和form表單,預設都是urlencoded格式
3 如果上傳檔案:form表單指定格式,ajax要使用Formdata物件

4 如果編碼方式是urlencoded格式,放到body體中資料格式如下
	username=Hammer&password=123  # post請求,Ajax預處理後得資料格式,urlencoded資料格式
    
5 如果是formdata編碼格式,body體中是:兩部分,資料和檔案
	
6 如果是json格式,body體中的格式是:就是json格式字串
	-注意:注意:注意:如果這種格式,request.POST取不到值了
	

上傳檔案

前面我們介紹到上傳檔案可以通過form表單來上傳檔案,通過input元素修改type=file就上傳單個檔案,如果加multiple引數就可以上傳多個檔案等····

form表單上傳檔案

<h1>form表單上傳檔案</h1>
<form action="" method="post" enctype="multipart/form-data">
    <p>使用者名稱:<input type="text" name="name"></p>
    <p>檔案:<input type="file" name="myfile"></p>
    <input type="submit" value="提交">
</form> 

def file_upload(request):
    if request.method=='GET':
        return render(request,'file_upload.html')
    else:
        myfile=request.FILES.get('myfile')
        with open(myfile.name,'wb') as f:
            for line in myfile:
                f.write(line)
        return HttpResponse('ok')

現在我們可以使用ajax上傳檔案,那麼格式和編碼有什麼要求?例項如下:

<h1>ajax上傳檔案</h1>
<p>使用者名稱:<input type="text" id="id_name"></p>
<p>檔案:<input type="file" id="id_myfile"></p>
<button id="id_btn">提交</button>


<script>
    $('#id_btn').click(function () {
        //如果要上傳檔案,需要藉助於一個js的FormData物件
        var formdata = new FormData() //例項化得到一個FormData物件
        formdata.append('name', $('#id_name').val()) //追加了一個name對應填入的值
        //追加檔案
        var file = $('#id_myfile')[0].files[0]
        formdata.append('myfile', file)
        $.ajax({
            url: 'file_upload',
            method: 'post',
            //上傳檔案,一定要注意如下兩行
            processData: false,  //不預處理資料,
            contentType: false,  //不指定編碼格式,使用formdata物件的預設編碼就是formdata格式
            data: formdata,
            success: function (data) {
                console.log(data)
            }
        })
    })
</script>
def file_upload(request):
    if request.method=='GET':
        return render(request,'file_upload.html')
    else:
        name=request.POST.get('name')
        myfile=request.FILES.get('myfile')
        print(type(myfile)) # 檢視型別
        from django.core.files.uploadedfile import InMemoryUploadedFile
        with open(myfile.name,'wb') as f:
            for line in myfile:
                f.write(line)

        return HttpResponse('上傳成功')

先拿到input元素:$(‘#id_myfile’)[0]

再拿到所有檔案:$(‘#id_myfile’)[0].files

再通過索引取出要取得檔案:$(‘#id_myfile’)[0].files[0]

image

總結

  • 如果要上傳檔案,需要藉助於一個js的FormData物件
  • Ajax上傳區域性重新整理
  • Ajax上傳檔案如果不想使用urlencoded預設處理,可以通過 processData: false不預處理,contentType: false不指定編碼格式

Ajax上傳json格式

注意:json模組在3.5版本之前不可以直接loads二進位制格式(bytes),在3.6版本以後可以

Ajax傳json格式只需指定編碼格式和序列化資料就能上傳

後端需要注意得是post請求得從body體裡取資料,然後反序列化即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>

<h1>ajax提交json格式</h1>


<p>使用者名稱: <input type="text" id="id_name"></p>
<p>密碼: <input type="password" id="id_password"></p>
<button id="id_button">提交</button>
</body>

<script>

    $('#id_button').click(function () {

        $.ajax({
            url: '/ajax_json/',
            method: 'post',
            contentType: 'application/json',  //指定編碼格式
            data: JSON.stringify({name:$('#id_name').val(),password:$('#id_password').val()}), //json格式字串,序列化資料
            success: function (data) {
                console.log(data)
            }
        })
    })
</script>
</html>

後端

def ajax_json(request):
    if request.method=='GET':
        return render(request,'ajax_json.html')

    else:
        # json格式,從POST中取不出來
        name=request.POST.get('name') 
        print(type(request.POST))  # 返回QueryDict物件,不允許修改
        # from  django.http.request import QueryDict
        print(name) # None 
        
        # 在body體中,bytes格式
        # django預設只處理兩種格式資料urlencode和form-data,json格式需要自己處理
        import json
        request.data=json.loads(request.body)  # 反序列化,從前端獲取資料
        
        name=request.data.get('name')
        password=request.data.get('password')
        print(name)
        print(password)
        return HttpResponse('ok')

django內建序列化

django提供了一個模組可以將物件直接序列化,然後返回給前端,但是可擴充套件性低,欄位不能控制,返回得是一個整體

from django.core import serializers
	def user_list(request):
        user_list = models.User.objects.all() # queryset物件
      res = serializers.serialize('json', user_list)
    return HttpResponse(res)

現在想要欄位可控,可以採用for迴圈列表套字典得格式,然後序列化

def user_list(request):
        user_list = models.User.objects.all() # queryset物件
        l = []
        for user in user_list:
            l.append({'name':user.name,'password':user.password})
        return JsonResponse(l,safe=False)  # 返回給前端
    # 如果使用json模組不需要加safe引數

ps:可以通過json.cn將序列化的資料轉成物件


【待續】

相關文章