Django從入門到放棄三 -- cookie,session,cbv加裝飾器,ajax,django中間件等

學習地址:https://www.cnblogs.com/liuqingzheng/p/9506212.html

一、CBV寫法 ( class base views )  一般指在views.py文件中定義的各種類

       FBV ( function base views ) 一般指在views.py文件中定義的各種函數

1.1、CBV寫法例子:

在urls.py文件中:
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),

    # CBV測試,訪問views.py文件下的CBTtest類,需要使用"CBTtest.as_view()"的方法
    path('test/', views.CBTtest.as_view()),    # as_view()拿到的是CBTtest類裏面某個函數的名稱[詳細信息查看View類裏面的as_view函數,最終返回一個get|post函數的內存地址]以及一個request對象。

]

# 在views.py文件中:

from django.views import View   # View類
class CBTtest(View):

    # GET請求訪問這個函數
    def get(self,request):
        print(request.method)
        return render(request, 'test.html')

    # POST請求訪問這個函數
    def post(self,request):
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            return HttpResponse('登錄成功')
        else:
            return render(request, 'test.html')

# test.html
<form action="" method="post">
    <p>用戶名:<input type="text" name="name"></p>
    <p>密碼:<input type="text" name="pwd"></p>
    <p><input type="submit" value="提交"></p>
</form>

1.2、CBV寫法總結

    1、定義請求方式的函數:函數名必須爲父類View裏面定義的http_method_names的名稱。
    2、http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    3、根據前臺發過來的請求的方式"GET或者POST"請求,就會響應對應名爲"GET或者POST"的函數(每次響應一個函數)。
    4、"GET或者POST"的函數寫法與FBV的函數寫法一致。
    5、在URL裏面配置 "path('test/', views.CBTtest.as_view())"

 

二、cookie的使用

2.1、什麼是cookie

    Cookie是key-value結構,類似於一個python中的字典。隨着服務器端的響應發送給客戶端瀏覽器。然後客戶端瀏覽器會把Cookie保存起來,當下一次再訪問服務器時把Cookie再發送給服務器。 Cookie是由服務器創建,然後通過響應發送給客戶端的一個鍵值對。客戶端會保存Cookie,並會標註出Cookie的來源(哪個服務器的Cookie)。當客戶端向服務器發出請求時會把所有這個服務器Cookie包含在請求中發送給服務器,這樣服務器就可以識別客戶端了!

 

2.2、Django中操作Cookie

  獲取Cookie:
     request.COOKIES['key']
     request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

  參數:
     default: 默認值
     salt: 加密鹽
     max_age: 後臺控制過期時間

 

   2.2.1、小例子  --  使用cookie判斷用戶的登錄狀態,判斷用戶是否需要重新登錄


# 在views.py文件中
# 寫一個功能,如果用戶登錄時cookie數據驗證失敗則重新登錄一次。
def test_cookie_login(request):

    if request.method == 'POST':
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            obj=redirect('/index/')             
            obj.set_cookie('is_login',True)
            obj.set_cookie('name',name)
            return obj

    return render(request,'test.html')

# index函數通過判斷用戶的cookie是否在客戶端瀏覽器上存在(沒有過期),用戶是否需要再次登錄。
def index(request):
    print(request.COOKIES)
    is_login=request.COOKIES.get('is_login')
    name=request.COOKIES.get('name')
    if is_login:  # 如果cookie驗證成功就返回首頁
        return render(request,'index.html',{'name':name})
    else:         # 否則就返回登錄頁面
        return redirect('/test_cookie_login/')

# 在urls.py文件中
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # cookie與session
    path('test_cookie_login/', views.test_cookie_login),
    re_path('^index/$', views.index ),

]

 

   2.2.2、小例子  --  用戶當前訪問的頁面(/aaa/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/aaa/)

# 在views.py文件中
# 用戶登錄裝飾器
def login_auth(func):
    def inner(request,*args,**kwargs):
        next_url=request.get_full_path()            # 用戶當前訪問的頁面(/test_order/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/test_order/)
        is_login = request.COOKIES.get('is_login')
        if is_login:
            return func(request,*args,**kwargs)
        else:
            # 此時用戶訪問的地址格式爲:http://127.0.0.1:8000/test_cookie_login/?next=/test_order/
            return redirect('/test_cookie_login/?next=%s' %next_url)  # 記錄下用戶當前訪問的頁面,傳值給"next"
    return inner


# 寫一個功能,如果用戶登錄時cookie數據驗證失敗則重新登錄一次。
def test_cookie_login(request):
    if request.method == 'POST':
        url = request.GET.get('next')     # 通過request獲取到數據"?next=/test_order/",拿到關鍵字'next'對應的數據"/test_order/"
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            obj=redirect(url)             # 用戶在登錄成功後,直接重定向到"/test_order/"目錄下(這個目錄是用戶一開始就想要訪問的目錄)
            obj.set_cookie('is_login',True)
            obj.set_cookie('name',name)
            return obj
    return render(request,'test.html')


# 默認網站的首頁,訪問的時候需要先登錄
@login_auth  # index=login_auth(index)
def index(request):
    name=request.COOKIES.get('name')
    return render(request,'index.html',{'name':name})  # index.html爲網站首頁


# 用戶當前訪問的頁面(/test_order/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/test_order/)
@login_auth
def test_order(request):
    return render(request,'order.html')  # order.html 爲購物車頁面


# 在urls.py文件中
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # re_path('^index/$', views.index ),

    # cookie與session
    re_path('test_cookie_login/', views.test_cookie_login),
    re_path('^index/$', views.index ),
    re_path('^test_order/$', views.test_order ),

]

 

 2.2.3、cookie的其他用法

    obj.set_cookie('is_login',True,max_age=5)     :設置cookie的緩存時間
    obj.set_cookie('is_login',True,path='/index/')   :表示指定cookie的路徑(訪問此路徑時會產生cookie),非此路徑將不產生cookie。
    obj.delete_cookie('is_login')                            :刪除一個cookie(指定cookie的"key")

 

三、session的使用

3.1、什麼是session?

    Cookie雖然在一定程度上解決了“保持狀態”的需求,但是由於Cookie本身最大支持4096字節,以及Cookie本身保存在客戶端,可能被攔截或竊取,因此就需要有一種新的東西,它能支持更多的字節,並且他保存在服務器,有較高的安全性。這就是Session。

 

3.2、Django中Session相關方法

    3.2.1、Session的生成

def test_session(request):

    # 客戶端A,生成A_session
    request.session['username']='szq'    # 生成一個session_key與session_data

    # 客戶端B,生成B_session
    request.session['age'] = '18'        # 生成一個session_key與session_data
    request.session['sex']='male'        #  生成一個session_data與request.session['age'] = '18'公用一個session_key

    '''
    request.session['username']='szq'       # 執行的操作的有:    
    
        1.生成隨機字符串session_key(這個字符串是返回給客戶端的,在不同的客戶端會生成不同的生成隨機字符串session_key)    
            # 在數據庫內對應的就是:session_key(一個session_key裏面可以包含多個字典)
            
        2.在django表裏生成一條記錄                                
            # 在數據庫內對應的就是:session_data(一個字典類型的數據),數據是加密後的{'username':'szq'}
            
        4.把(session_key)隨機字符串返回到客戶端瀏覽器上
    '''

    return HttpResponse('ok')

    session在數據庫內存在的格式如下:

 

    3.2.1、Session的操作(增刪改查等操作)


# 設置session['username']的值爲123456
request.session['username'] = 123456

# 獲取session['username']的值"szq"
request.session['username']       

# 刪除session['username']這個key及對應的value
del request.session['username']          

# 獲取"session['key']"的值:dict_keys(['sex', 'username', 'age'])
request.session.keys()            

# 獲取"session['value']"的值:dict_values(['male', 'szq', '18'])
request.session.values()          

 # 獲取"session['key:value']"的值:dict_items([('sex', 'male'), ('username', 'szq'), ('age', '18')])
request.session.items()          

# 獲取當前客戶端連接服務器會話session的key"of1op71gr1qaf9sj8ey31gmeehlkrynk"
request.session.session_key   

# 檢查會話session的key在數據庫中是否存在
request.session.exists("session_key")  

#刪除當前會話的所有Session數據(只刪數據庫,不刪除cookie)
request.session.delete()                     

# 刪除當前的會話數據並刪除會話的Cookie(數據庫和cookie都刪)
request.session.flush()                       
        # 這用於確保前面的會話數據不可以再次被用戶的瀏覽器訪問
        # 例如,django.contrib.auth.logout() 函數中就會調用它。

# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
    * 如果value是個整數,session會在些秒數後失效,再次訪問時會生成一個新的session
    * 如果value是個datatime或timedelta,session就會在這個時間後失效,再次訪問時會生成一個新的session
    * 如果value是0,用戶關閉瀏覽器session就會失效,再次訪問時會生成一個新的session
    * 如果value是None,session會依賴全局session失效策略。

 

3.3、Django中的Session配置

# 公用設置項:放在settings.py文件中
SESSION_COOKIE_NAME = "sessionid"                # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認)
SESSION_COOKIE_PATH = "/"                        # Session的cookie保存的路徑(默認)
SESSION_COOKIE_DOMAIN = None                      # Session的cookie保存的域名(默認)
SESSION_COOKIE_SECURE = False                     # 是否Https傳輸cookie(默認)
SESSION_COOKIE_HTTPONLY = True                    # 是否Session的cookie只支持http傳輸(默認)
SESSION_COOKIE_AGE = 1209600                      # Session的cookie失效日期(2周)(默認)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False           # 是否關閉瀏覽器使得Session過期(默認)
SESSION_SAVE_EVERY_REQUEST = False                # 是否每次請求都保存Session,默認修改之後才保存(默認)

 

四、cbv加裝飾器  --  1.在CBV內部的函數加裝飾器 2.直接在類上使用裝飾器(需要指定實例化對象的名稱)

# 在urls.py文件中

urlpatterns = [
    path('admin/', admin.site.urls),

    # CBV裝飾器測試
    path('test_cbv/', views.CBTtest.as_view()),
    re_path('test_cookie_login/', views.test_cookie_login),

]

# 在views.py文件中
# 用戶登錄裝飾器
def login_auth(func):
    def inner(request, *args, **kwargs):
        next_url = request.get_full_path()  # 用戶當前訪問的頁面(/test_cbv/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/test_cbv/)
        is_login = request.COOKIES.get('is_login')
        if is_login:
            return func(request, *args, **kwargs)
        else:
            # 此時用戶訪問的地址格式爲:http://127.0.0.1:8000/test_cookie_login/?next=/test_cbv/
            return redirect('/test_cookie_login/?next=%s' % next_url)  # 記錄下用戶當前訪問的頁面,傳值給"next"
    return inner


# 登錄接口
def test_cookie_login(request):
    if request.method == 'POST':
        url = request.GET.get('next')         # 通過request獲取到數據"?next=/test_cbv/",拿到關鍵字'next'對應的數據"/test_cbv/"
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            obj = redirect(url)             # 用戶在登錄成功後,直接重定向到"/test_cbv/"目錄下(這個目錄是用戶一開始就想要訪問的目錄)
            obj.set_cookie('is_login',True)
            obj.set_cookie('name',name)
            return obj
    return render(request,'login.html')


# CBV裝飾器的用法及寫法--方式一(在類實例化的函數裏使用)
from django.views import View
from django.utils.decorators import method_decorator


class CBTtest(View):
    # GET請求訪問這個函數
    @method_decorator(login_auth)
    def get(self,request):
        print(request.method)
        return render(request, 'login.html')

    # POST請求訪問這個函數
    @method_decorator(login_auth)
    def post(self,request):
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            return HttpResponse('登錄成功')
        else:
            return render(request, 'login.html')

# CBV裝飾器的用法方式二(在類上面印有)
@method_decorator(login_auth,name='get')
@method_decorator(login_auth,name='post')
class CBTtest(View):
    # GET請求訪問這個函數
    def get(self,request):
        print(request.method)
        return render(request, 'login.html')

    # POST請求訪問這個函數
    def post(self,request):
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            return HttpResponse('登錄成功')
        else:
            return render(request, 'login.html')

 

五、ajax提交數據,提交json格式數據

  5.1、什麼是ajax?

   1、ajax(Asynchronous Javascript And XML)翻譯成中文就是“異步Javascript和XML”。即使用Javascript語言與服務器進行異步交互,傳輸的數據爲XML(當然,傳輸的數據不只是XML,現在更多使用json數據)。
      同步交互:客戶端發出一個請求後,需要等待服務器響應結束後,才能發出第二個請求;
      異步交互:客戶端發出一個請求後,無需等待服務器響應結束,就可以發出第二個請求。
2、ajax除了異步的特點外,還有一個就是:瀏覽器頁面局部刷新;(這一特點給用戶的感受是在不知不覺中完成請求和響應過程)

 

5.2、ajax應用場景?

 

5.3、ajax的優點

    1、ajax使用Javascript技術向服務器發送異步請求
    2、ajax無須刷新整個頁面

 

5.4、ajax使用例子1 --  使用ajax(GET|POST)請求一個登陸界面

        後端獲取數據時,使用request.GET.get或者request.POST.get的方式獲取數據。

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # 測試AJAX
    re_path('test_ajax/', views.test_ajax),
    re_path('ajax/', views.ajax),
    re_path('index/', views.index),
]

# views.py文件中
import json
def test_ajax(request):
    return render(request,'ajax.html')

def ajax(request):
    bak_msg={'user':None,'msg':'用戶名密碼錯誤'}

    # 前端ajax使用get或者post方式請求數據,後端使用request.GET|POST.get的方式獲取數據
    name=request.GET.get('name')                  
    pwd=request.GET.get('pwd')
    if name == 'szq' and pwd == '123':
        bak_msg['user']=name
        bak_msg['msg']='登錄成功'
    return HttpResponse(json.dumps(bak_msg))

def index(request):
    return render(request,'index.html')

   前端ajax.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    <p>用戶名:<input type="text" name="name" id="name"></p>
    <p>密碼:<input type="text" name="pwd"></p>
{#    <p><input type="submit" value="提交"></p>#}
</form>

<button id="btnl">提交</button>

{#針對服務端,是一個異步的請求#}
<script>

    $('#btnl').click(function () {
        var name=$('#name').val()            {# 獲取用戶名信息 #}
        var pwd=$('[name="pwd"]').val()      {# 獲取密碼信息 #}
        $.ajax({
            url: '/ajax/',                   {# 請求到服務器的URL #}
            type: 'get',                     {# 給服務器發送的請求方式,後端取數據需要從request.GET(POST)裏面取數據 #}
            data:{'name':name,'pwd':pwd},    {# 請求服務器時攜帶的信息(用戶名和密碼) #}
            success: function (data) {       {# data拿到的是服務端"ajax"請求的返回結果(是一個異步的返回結果) #}
                {# alert(data)               {# 拿到服務器數據後的返回值 #}
                var msg=JSON.parse(data);    {# 拿到後端返回的json格式的數據,並轉譯 #}
                if (msg.user){               {# 如果用戶存在,表示登錄成功 #}
                    location.href='/index/'  {# 登錄成功後,重定向到'/index/'頁面 #}
                }else {
                    alert(msg.msg)           {# 登錄失敗返回的結果 #}
                }
            }
        })
    })

</script>

</body>
</html>

 

5.4、ajax使用例子2 --  使用ajax(JSON格式)請求一個登陸界面

        後端獲取數據時,使用request.body的方式獲取數據。

        後端獲取數據時,需要將獲取到的二進制字符串轉譯。

        後端獲取數據時,需要把json格式的字符串轉譯成Python格式的。

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # 測試AJAX
    re_path('test_ajax/', views.test_ajax),
    re_path('ajax/', views.ajax),
    re_path('index/', views.index),
]

# views.py文件中
import json
def test_ajax(request):
    return render(request,'ajax.html')

def ajax(request):
    print(request.body)
    # b'{"name":"szq","pwd":"123"}' 獲取到前端請求的數據(包含用戶名和密碼),是一個二進制格式,需要轉譯
    # 這裏不是通過request.POST獲取到數據了

    res=request.body.decode('utf-8')   # 把二進制格式的數據轉譯成正常
    res_dic=json.loads(res)            # 把json格式的數據轉換成python格式

    bak_msg={'user':None,'msg':'用戶名密碼錯誤'}
    if res_dic['name'] == 'szq' and res_dic['pwd'] == '123':
        bak_msg['user']=res_dic['name']
        bak_msg['msg']='登錄成功'


    return HttpResponse(json.dumps(bak_msg))

def index(request):
    return render(request,'index.html')

   前端ajax.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    <p>用戶名:<input type="text" name="name" id="name"></p>
    <p>密碼:<input type="text" name="pwd"></p>
{#    <p><input type="submit" value="提交"></p>#}
</form>

<button id="btnl">提交</button>

{#針對服務端,是一個異步的請求#}
<script>

    $('#btnl').click(function () {
        var name=$('#name').val()            {# 獲取用戶名信息 #}
        var pwd=$('[name="pwd"]').val()      {# 獲取密碼信息 #}
        $.ajax({
            url: '/ajax/',                   {# 請求到服務器的URL #}
            type: 'post',                    {# 給服務器發送的請求方式 #}

            {# 如果需要向後臺提交json格式的字符串時,需要指定:contentType:'application/json' #}
            {# 當contentType爲json格式的時候,後端取數據需要從request.body裏面取數據 #}
            contentType:'application/json',

            {# 請求服務器時攜帶的信息(用戶名和密碼)json格式的字符串 #}
            {# 如果不加"JSON.stringify"那麼後端獲取到的數據格式就爲b'name:szq,pwd:123',加上"JSON.stringify"之後,後端獲取到的數據格式就爲b'{"name":"szq","pwd":"123"}'#}
            data:JSON.stringify({'name':name,'pwd':pwd}),


            success: function (data) {       {# data拿到的是服務端"ajax"請求的返回結果(是一個異步的返回結果) #}
                {#alert(data)                {# 拿到服務器數據後的返回值 #}
                var msg=JSON.parse(data);    {# 拿到後端返回的json格式的數據,並轉譯 #}
                if (msg.user){               {# 如果用戶存在,表示登錄成功 #}
                    location.href='/index/'  {# 登錄成功後,重定向到'/index/'頁面 #}
                }else {
                    alert(msg.msg)           {# 登錄失敗返回的結果 #}
                }
            }
        })
    })

</script>

</body>
</html>

 

  5.5、Ajax---->服務器------>Ajax執行流程圖

 

六、基於form表單上傳文件

       前端form表單需要指定:enctype="multipart/form-data"

       後端獲取form表單上傳文件內容時,需要用"request.FILES.get"

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # from表單上傳文件
    re_path('file_upload/', views.file_upload),
]

# 在views.py文件中
def file_upload(request):
    if request.method == "POST":

        # 上傳一個圖片時返回的數據(字典類型):<MultiValueDict: {'myfile': [<InMemoryUploadedFile: DDD.jpg (image/jpeg)>]}>
        print(request.FILES)

        # 查看這個字典類型數據裏面'myfile'這個key對應的value的值的類型:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        print(type(request.FILES.get('myfile')))
        # 查看源碼,InMemoryUploadedFile類
        from django.core.files.uploadedfile import InMemoryUploadedFile

        # 通過key拿到對應圖片的數據內容
        myfile=request.FILES.get('myfile')

        # 通過圖片的數據內容拿到圖片的文件名
        file_name=myfile.name
        print(file_name)        # bbb.jpg

        # 保存這個圖片到服務器
        with open(file_name,'wb')as f:
            for line in myfile:           # 這裏循環myfile這個文件
                f.write(line)

        return HttpResponse('文件上傳成功')

    return render(request,'file_upload.html')

  前端'file_upload.html'文件內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post" enctype="multipart/form-data">   {# 使用form表單上傳數據時的固定用法 #}
    <p>用戶名:<input type="text" name="name" id="name"></p>
    <p>文件:<input type="file" name="myfile"></p>
    <p><input type="submit" value="form表單提交"></p>
</form>

</body>
</html>

 

七、基於ajax上傳文件

       前端ajax:var formdata=new FormData

       後端獲取form表單上傳文件內容時,需要用"request.FILES.get"

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # from表單上傳文件
    re_path('file_upload/', views.file_upload),
]

# 在views.py文件中
def file_upload(request):
    if request.method == "POST":

        # 上傳一個圖片時返回的數據(字典類型):<MultiValueDict: {'myfile': [<InMemoryUploadedFile: DDD.jpg (image/jpeg)>]}>
        print(request.FILES)

        # 查看這個字典類型數據裏面'myfile'這個key對應的value的值的類型:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        print(type(request.FILES.get('myfile')))
        # 查看源碼,InMemoryUploadedFile類
        from django.core.files.uploadedfile import InMemoryUploadedFile

        # 通過key拿到對應圖片的數據內容
        myfile=request.FILES.get('myfile')

        # 通過圖片的數據內容拿到圖片的文件名
        file_name=myfile.name
        print(file_name)        # bbb.jpg

        # 保存這個圖片到服務器
        with open(file_name,'wb')as f:
            for line in myfile:           # 這裏循環myfile這個文件
                f.write(line)

        return HttpResponse('文件上傳成功')

    return render(request,'file_upload.html')

 前端'file_upload.html'文件內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post" enctype="multipart/form-data">   {# 使用form表單上傳數據時的固定用法 #}
    <p>用戶名:<input type="text" name="name" id="name"></p>
    <p>文件:<input type="file" name="myfile" id="myfile"></p>
</form>

<button id="btnl">ajax提交</button>

<script>

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

        var formdata=new FormData;
        formdata.append('name',$("#name").val());
        formdata.append('myfile',$("#myfile")[0].files[0]);

        $.ajax({
            url: '/file_upload/',
            type: 'post',
            contentType:false,
            processData:false,
            data:formdata,
            success: function (data) {
                alert(data)
            }
        })
    })

</script>

</body>
</html>

 

八、django中間件

8.1、什麼是中間件?

       中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全局上改變django的輸入與輸出。因爲改變的是全局,所以需要謹慎實用,用不好會影響到性能。

 

8.2、中間件有什麼用?  用戶所有的請求都會先經過中間件,然後到達URL路由層,用戶拿到數據的返回結果之後,也是先經過中間件(倒序),然後到達用戶。

       1、如果你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些都可以通過中間件來實現。
      2、可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。
      3、Django默認的中間件:(在django項目的settings模塊中,有一個 MIDDLEWARE_CLASSES 變量,其中每一個元素就是一箇中間件,如下

# 每一箇中間件都有具體的功能
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

    4、當用戶發起請求的時候會依次經過所有的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。

 

8.3、自定義中間件

# 在settings.py文件中的MIDDLEWARE自定義中間件(app01目錄下的mytest文件)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    # 自定義中間件
    'app01.mytest.MyMid1',
    'app01.mytest.MyMid2',
]

# mytest.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.utils.deprecation import MiddlewareMixin


class MyMid1(MiddlewareMixin):

    def process_request(self,request):
        print('MyMid1 --- process_request')
        # return HttpResponse('ok')

    def process_response(self,request,response):   # response是一個<class 'django.http.response.HttpResponse'>
        print('MyMid1 --- process_response')
        return response                            # 用戶請求後,必須要一個response數據返回

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('MyMid1 --- process_view')

class MyMid2(MiddlewareMixin):

    def process_request(self,request):
        print('MyMid2 --- process_request')

    def process_response(self,request,response):    # response是一個<class 'django.http.response.HttpResponse'>
        print('MyMid2 --- process_response')
        return response                             # 用戶請求後,必須要一個response數據返回

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('MyMid2 --- process_view')

# 返回結果就是中間件的執行順序
# MyMid1 --- process_request
# MyMid2 --- process_request
# MyMid1 --- process_view
# MyMid2 --- process_view
# MyMid2 --- process_response
# MyMid1 --- process_response

 下圖進行分析上面mytest.py文件的過程:

當最後一箇中間的process_request到達路由關係映射之後,返回到中間件1的process_view,然後依次往下,到達views函數,最後通過process_response依次返回到達用戶。

 

九、中間件應用場景
    9.1、做IP訪問頻率限制
    某些IP訪問服務器的頻率過高,進行攔截,比如限制每分鐘不能超過20次。

    9.2、URL訪問過濾
    如果用戶訪問的是login視圖(放過)
    如果訪問其他視圖,需要檢測是不是有session認證,已經有了放行,沒有返回login,這樣就省得在多個視圖函數上寫裝飾器了!

    9.3、作爲延伸擴展內容,有餘力的同學可以嘗試着讀一下以下兩個自帶的中間件:
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',

 

十、CSRF_TOKEN跨站請求僞造

使用Django自帶的中間件:'django.middleware.csrf.CsrfViewMiddleware' 可以防止跨站請求僞造。

原理:在前端HTML頁面加上"{% csrf_token %}",那麼在訪問的時候,會生成一個"<input type="hidden" name="csrfmiddlewaretoken" value="ODrQkcqNbmyYMREnjoCGUjwBuS0Cfz4m4VwzktZh1ryidjHUEFajE3BwdEgHpcWw">"隨機字符串,每次訪問生成的字符串都是不同的。前端HTML頁面在POST提交時會攜帶這個隨機字符串,然後提交到後端服務器,服務器會確認此次請求是否有這個隨機字符串,如果有則可以訪問,如果沒有訪問失敗。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章