Django之中間件與form其餘用法

<span style = "color:red">中間件經常使用的五種方法:</span>

  • <span style = "color:green">process_request(self,request)</span>html

    • process_request有一個參數,就是request,與視圖函數中的request同樣。它的返回值能夠是None也能夠是HttpResponse對象。返回值是None,按正常流程繼續走,交給下一個中間件處理,若是是HttpResponse對象,Django將不執行視圖函數,而將相應對象返回給瀏覽器。前端

    • <span style = "color:orangered">示例:</span>python

      應用下自定義一個文件夾,自定義一個py文件:
      from django.utils.deprecation import MiddlewareMixin
      class MD1(MiddlewareMixin):
          def process_request(self,request):
              print("MD1的process_request")
      class MD2(MiddlewareMixin):
          def process_request(self,request):
              print("MD2的process_request")
      
      settings配置:
      在MIDDLEWARE列表中加入
      'app01.mymiddleware.my.MD1',
      'app01.mymiddleware.my.MD2',
      
      執行結果是:
      MD1的process_request
      MD2的process_request
  • <span style = "color:green">process_response(self,request,reponse)</span>jquery

    • 多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最早執行。另外,他有兩個參數,一個是request,一個是response,response是視圖函數返回的HttpResponse對象。該方法的返回值必須是HttpResponse對象。ajax

    • process_response方法是在視圖函數以後執行的。數據庫

    • <span style = "color:orangered">示例:</span>django

      from django.utils.deprecation import MiddlewareMixin
      
      
      class MD1(MiddlewareMixin):
      
          def process_request(self, request):
              print("MD1裏面的 process_request")
              #沒必要須寫return值
          def process_response(self, request, response):#request和response兩個參數必須有,名字隨便取
              print("MD1裏面的 process_response")
              #print(response.__dict__['_container'][0].decode('utf-8')) #查看響應體裏面的內容的方法,或者直接使用response.content也能夠看到響應體裏面的內容,因爲response是個變量,直接點擊看源碼是看不到的,你打印type(response)發現是HttpResponse對象,查看這個對象的源碼就知道有什麼方法能夠用了。
           return response  #必須有返回值,寫return response  ,這個response就像一個接力棒同樣
              #return HttpResponse('瞎搞') ,若是你寫了這個,那麼你視圖返回過來的內容就被它給替代了
      
      class MD2(MiddlewareMixin):
          def process_request(self, request):
              print("MD2裏面的 process_request")
              pass
      
          def process_response(self, request, response): #request和response兩個參數必需要有,名字隨便取
              print("MD2裏面的 process_response") 
              return response  #必須返回response,否則你上層的中間件就沒有拿到httpresponse對象,就會報錯
      
      
      settings配置與上方示例一致
      
      打印的結果是:
      MD1裏面的process_request
      MD2裏面的process_request
      MD2裏面的process_response
      MD1裏面的process_response
  • <span style = "color:green">process_view(self,request,view_func,view_args,view_kwargs)</span>瀏覽器

    • 該方法有四個參數:服務器

      • request是HTTPRequest對象cookie

      • view_func是Django即將使用的視圖函數。(它是實際的函數對象,而不是函數的名稱做爲字符串。)

      • view_args是將傳遞給視圖的位置參數的列表

      • view_kwargs是將傳遞給視圖的關鍵字參數的字典,view_args與view_kwargs都不包含第一個視圖函數(request).

      • <span style = "color:purple">Django會在調用視圖函數以前調用process_view方法。</span>

      • 它應該返回None或者一個HttpResponse對象。若是返回None,Django將繼續處理這個請求,執行任何其餘中間件的process_view方法,而後在執行相應的視圖。若是它返回一個HttpResponse對象,django不會調用對象的視圖函數。它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。

      • 流程圖:<img src="https://img-blog.csdnimg.cn/20191202195938307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

      • <span style = "color:orangered">示例:</span>

        from django.utils.deprecation import MiddlewareMixin
        
        
        class MD1(MiddlewareMixin):
        
            def process_request(self, request):
                print("MD1裏面的 process_request")
        
            def process_response(self, request, response):
                print("MD1裏面的 process_response")
                return response
        
            def process_view(self, request, view_func, view_args, view_kwargs):
                print("-" * 80)
                print("MD1 中的process_view")
                print(view_func, view_func.__name__) #就是url映射到的那個視圖函數,也就是說每一箇中間件的這個process_view已經提早拿到了要執行的那個視圖函數
                #ret = view_func(request) #提早執行視圖函數,不用到了上圖的試圖函數的位置再執行,若是你視圖函數有參數的話,能夠這麼寫 view_func(request,view_args,view_kwargs) 
                #return ret  #直接就在MD1中間件這裏這個類的process_response給返回了,就不會去找到視圖函數裏面的這個函數去執行了。
        
        class MD2(MiddlewareMixin):
            def process_request(self, request):
                print("MD2裏面的 process_request")
                pass
        
            def process_response(self, request, response):
                print("MD2裏面的 process_response")
                return response
        
            def process_view(self, request, view_func, view_args, view_kwargs):
                print("-" * 80)
                print("MD2 中的process_view")
                print(view_func, view_func.__name__)
        
        
        settings配置如上:
        執行結果是:
        MD1裏面的 process_request
        MD2裏面的 process_request
        MD1 中的process_view
        <function index at 0x000001DE68317488> index
        MD2 中的process_view
        <function index at 0x000001DE68317488> index
        MD2裏面的 process_response
        MD1裏面的 process_response
      • process_view方法是在process_request以後,reprocess_response以前,視圖函數以前執行的,執行順序按照MIDDLEWARE中的註冊順序從前到後順序執行的,如圖<img src="https://img-blog.csdnimg.cn/20191202202449943.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

  • <span style = "color:green">process_exception(self,request,exception)</span>

    • 該方法兩個參數:

      • 一個HttpRequest對象

      • 一個exception是視圖函數異常產生的Exception對象。

      • 這個方法只有在視圖函數中出現異常了才執行,它返回的值能夠是一個None,也能夠是一個HttpResponse對象。若是是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給瀏覽器,不然將默認處理異常。若是返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件註冊順序的倒序執行。

      • 流程圖:<img src="https://img-blog.csdnimg.cn/20191202210441463.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

      • <span style = "color:orangered">示例:</span>

        from django.utils.deprecation import MiddlewareMixin
        
        
        class MD1(MiddlewareMixin):
        
            def process_request(self, request):
                print("MD1裏面的 process_request")
        
            def process_response(self, request, response):
                print("MD1裏面的 process_response")
                return response
        
            def process_view(self, request, view_func, view_args, view_kwargs):
                print("-" * 80)
                print("MD1 中的process_view")
                print(view_func, view_func.__name__)
        
            def process_exception(self, request, exception):
                print(exception)
                print("MD1 中的process_exception")
        
        
        
        class MD2(MiddlewareMixin):
            def process_request(self, request):
                print("MD2裏面的 process_request")
                pass
        
            def process_response(self, request, response):
                print("MD2裏面的 process_response")
                return response
        
            def process_view(self, request, view_func, view_args, view_kwargs):
                print("-" * 80)
                print("MD2 中的process_view")
                print(view_func, view_func.__name__)
        
            def process_exception(self, request, exception):
                print(exception)
                print("MD2 中的process_exception")
                return HttpResponse(str(exception))
        
        views.py代碼:#拋出異常,否則process_exception方法不執行
        def index(request):
            print("app01 中的 index視圖")
            raise ValueError("呵呵")
            return HttpResponse("O98K")
        settings配置如上:
        執行結果:
        MD1裏面的 process_request
        MD2裏面的 process_request
        MD1 中的process_view
        <function index at 0x0000022C09727488> index
        MD2 中的process_view
        <function index at 0x0000022C09727488> index
        app01 中的 index視圖
        呵呵
        MD2 中的process_exception
        MD2裏面的 process_response
        MD1裏面的 process_response
      • 注意,上述示例並無執行MD1的process_exception方法,由於MD2中的process_exception方法直接返回一個響應對象,如圖 <img src="https://img-blog.csdnimg.cn/20191202211835903.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

  • <span style = "color:green">process_template_response(self,request,response)</span>

    • 該方法用的比較少,它的參數,一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)。

    • process_template_response是在視圖函數執行完成後當即執行,可是它有一個前提條件,就是視圖函數返回的對象有一個render()方法(或者代表該對象是一個TemplateResponse對象或等價方法)

    • <span style = "color:orangered">示例:</span>

      class MD1(MiddlewareMixin):
      
          def process_request(self, request):
              print("MD1裏面的 process_request")
      
          def process_response(self, request, response):
              print("MD1裏面的 process_response")
              return response
      
          def process_view(self, request, view_func, view_args, view_kwargs):
              print("-" * 80)
              print("MD1 中的process_view")
              print(view_func, view_func.__name__)
      
          def process_exception(self, request, exception):
              print(exception)
              print("MD1 中的process_exception")
              return HttpResponse(str(exception))
      
          def process_template_response(self, request, response):
              print("MD1 中的process_template_response")
              return response
      
      
      class MD2(MiddlewareMixin):
          def process_request(self, request):
              print("MD2裏面的 process_request")
              pass
      
          def process_response(self, request, response):
              print("MD2裏面的 process_response")
              return response
      
          def process_view(self, request, view_func, view_args, view_kwargs):
              print("-" * 80)
              print("MD2 中的process_view")
              print(view_func, view_func.__name__)
      
          def process_exception(self, request, exception):
              print(exception)
              print("MD2 中的process_exception")
      
          def process_template_response(self, request, response):
              print("MD2 中的process_template_response")
              return response
      
      
      views.py文件代碼:
      def index(request):
          print("app01 中的 index視圖")
        #raise ValueError('出錯啦') 
          def render():
              print("in index/render")  
              #raise ValueError('出錯啦') #至於render函數中報錯了,那麼會先執行process_template_response方法,而後執行process_exception方法,若是是在render方法外面報錯了,那麼就不會執行這個process_template_response方法了。
              return HttpResponse("O98K") #返回的將是這個新的對象
          rep = HttpResponse("OK")
          rep.render = render
          return rep
      
      settings配置如上:
      執行結果是:
      MD1裏面的 process_request
      MD2裏面的 process_request
      --------------------------------------------------------------------------------
      MD1 中的process_view
      <function index at 0x000001EA6771BBF8> index
      --------------------------------------------------------------------------------
      MD2 中的process_view
      <function index at 0x000001EA6771BBF8> index
      MD2 中的process_template_response
      MD1 中的process_template_response
      MD2裏面的 process_response
      MD1裏面的 process_response
    • 從結果不難看出:視圖函數執行完以後,當即執行了中間件的process_template_response方法,順序是倒序,先執行MD2的,在執行MD1的,接着執行了視圖函數返回的HttpResponse對象的render方法,返回了一個新的HttpResponse對象,接着執行中間件的process_response方法。

<span style = "color:red">中間件執行流程:</span>

  • 請求到達中間件以後,先按照正序執行每一個註冊中間件的process_reques方法,process_request方法返回的值是None,就依次執行,若是返回的值是HttpResponse對象,再也不執行後面的process_request方法,而是執行當前對應中間件的process_response方法,將HttpResponse對象返回給瀏覽器。也就是說:若是MIDDLEWARE中註冊了6箇中間件,執行過程當中,第3箇中間件返回了一個HttpResponse對象,那麼第4,5,6中間件的process_request和process_response方法都不執行,順序執行3,2,1中間件的process_response方法。

    • 如圖: <img src="https://img-blog.csdnimg.cn/20191202214752679.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">
  • process_request方法都執行完後,匹配路由,找到要執行的視圖函數,先不執行視圖函數,先執行中間件中的process_view方法,process_view方法返回None,繼續按順序執行,全部process_view方法執行完後執行視圖函數。加入中間件3 的process_view方法返回了HttpResponse對象,則4,5,6的process_view以及視圖函數都不執行,直接從最後一箇中間件,也就是中間件6的process_response方法開始倒序執行。

    • 如圖: <img src="https://img-blog.csdnimg.cn/20191202214940655.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">
  • process_template_response和process_exception兩個方法的觸發是有條件的,執行順序也是倒序。總結全部的執行流程以下:

    • 如圖: <img src="https://img-blog.csdnimg.cn/20191202215226873.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

      <img src="https://img-blog.csdnimg.cn/20191202215349624.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

<span style = "color:red">中間件版登錄認證:</span>

  • 中間件版的登陸驗證須要依靠session,因此數據庫中要有django_session表。

  • 配置urls.py

    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^index/$', views.index),
        url(r'^login/$', views.login, name='login'),
    ]
  • 配置views.py

    from django.shortcuts import render, HttpResponse, redirect
    
    
    def index(request):
        return HttpResponse('this is index')
    
    
    def home(request):
        return HttpResponse('this is home')
    
    
    def login(request):
        if request.method == "POST":
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
    
            if user == "Q1mi" and pwd == "123456":
                # 設置session
                request.session["user"] = user
                # 獲取跳到登錄頁面以前的URL
                next_url = request.GET.get("next")
                # 若是有,就跳轉回登錄以前的URL
                if next_url:
                    return redirect(next_url)
                # 不然默認跳轉到index頁面
                else:
                    return redirect("/index/")
        return render(request, "login.html")
  • login.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>登陸頁面</title>
    </head>
    <body>
    <form action="{% url 'login' %}">
        <p>
            <label for="user">用戶名:</label>
            <input type="text" name="user" id="user">
        </p>
        <p>
            <label for="pwd">密 碼:</label>
            <input type="text" name="pwd" id="pwd">
        </p>
        <input type="submit" value="登陸">
    </form>
    </body>
    </html>
  • middlewares.py

    class AuthMD(MiddlewareMixin):
        white_list = ['/login/', ]  # 白名單
        balck_list = ['/black/', ]  # 黑名單
    
        def process_request(self, request):
            from django.shortcuts import redirect, HttpResponse
    
            next_url = request.path_info
            print(request.path_info, request.get_full_path())
    
            if next_url in self.white_list or request.session.get("user"):
                return
            elif next_url in self.balck_list:
                return HttpResponse('This is an illegal URL')
            else:
                return redirect("/login/?next={}".format(next_url))
  • settings.py中註冊

    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',
        'middlewares.AuthMD',
    ]
  • AuthMD中間件註冊後,全部的請求都要走AuthMD的process_request方法。

  • 訪問的URL在白名單內或者session中有user用戶名,則不作阻攔走正常流程;

  • 若是URL在黑名單中,則返回This is an illegal URL的字符串;

  • 正常的URL可是須要登陸後訪問,讓瀏覽器跳轉到登陸頁面。

  • <span style="color:red">注:AuthMD中間件中須要session,因此AuthMD註冊的位置要在session中間的下方。</span>

  • Django請求流程圖 <img src="https://img-blog.csdnimg.cn/20191202220437181.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY5MzM5Mw==,size_16,color_FFFFFF,t_70">

<span style = "color:red">Ajax經過csrf的第三種方式:即jQuery設置cookie</span>

<script src="{% static 'jquery.js' %}"></script>
<script src="{% static 'jquery.cookie.js' %}"></script>
<script>
    $('#btn').click(function () {
        var uname = $('[type="text"]').val();
        var pwd = $('[type="password"]').val();
        $.cookie('xx','sss');
        $.ajax({
            url:'/login/',
            type:'post',
            headers:{'X-CSRFToken':$.cookie('csrftoken')}, //設置請求頭
            data:{uname:uname,pwd:pwd,},
            success:function (res) {
                console.log(res);
            }
        })
    })
</script>

<span style = "color:red">csrf詳解:</span>

詳述CSRF(Cross-site request forgery),中文名稱:跨站請求僞造,也被稱爲:one click attack/session riding,縮寫爲:CSRF/XSRF。攻擊者經過HTTP請求將數據傳送到服務器,從而盜取回話的cookie。盜取回話cookie以後,攻擊者不只能夠獲取用戶的信息,還能夠修改該cookie關聯的帳戶信息。

  因此解決csrf攻擊的最直接的辦法就是生成一個隨機的csrftoken值,保存在用戶的頁面上,每次請求都帶着這個值過來完成校驗。

<span style = "color:red">form組件</span>

  • 做用:

    1. 生成頁面可用的HTML標籤
    2. 對用戶提交的數據進行校驗
    3. 保留上次輸入內容
  • 使用步驟:

    • 第一步:建立form類

      from django.shortcuts import render,HttpResponse
      from django import forms
      class LoginForm(forms.Form):
          username = forms.CharField(
              label="用戶名",
              required=True, #不能爲空
              max_length=7, #長度不能超過7個字符
              min_length=2, #最短不能低於2個字符
              initial="小騷浩", #初始值
              widget=forms.TextInput(attrs={'class':'c1',"placeholder":"請輸入用戶名"}),
              error_messages={
                  'required':"用戶名不能爲空",
                  'min_length':"過短了"
              }
          )
          password = forms.CharField(
              required=True,
              label="密碼",
              widget=forms.PasswordInput(attrs={'class':'c1',"placeholder":"請輸入密碼"}),
          )
          sex = forms.ChoiceField(
              choices=[(1,'男'),(2,"女")],
              widget=forms.RadioSelect(attrs={'xx':'None'}),
          )
          hobby = forms.MultipleChoiceField(
              choices=[(1,'逛街'),(2,'打遊戲'),(3,'燙頭')],
              widget=forms.CheckboxSelectMultiple,
          )
          bothday = forms.CharField(
              widget=forms.TextInput(attrs={'type':'date'})
          )
    • 第二步:在views中實例化這個類對象,並交給前端html頁面

      def login(request):
          if request.method == "GET":
              forms_obj = LoginForm()
              return render(request,"login.html",{'forms_obj':forms_obj})
          else:
              forms_obj = LoginForm(request.POST)
              status = forms_obj.is_valid() #開始校驗
              return render(request,'login.html',{'forms_obj':forms_obj})
    • 第三步:進行數據格式校驗

      forms_obj = LoginForm(request.POST)
              status = forms_obj.is_valid() #開始校驗
    • 第四步:login.html頁面

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>登陸頁面</title>
          <style>
              .c1{
                  background-color: orangered;
              }
              ul{
                  list-style: none;
              }
          </style>
      </head>
      <body>
      <form action="" method="post" novalidate>
          {% csrf_token %}
          <div>
              <label for="{{ forms_obj.username.id_for_label }}">{{ forms_obj.username.label }}</label>
              {{ forms_obj.username }}
              <span>{{ forms_obj.username.errors.0 }}</span>
          </div>
              <div>
              <label for="{{ forms_obj.password.id_for_label }}">{{ forms_obj.password.label }}</label>
                  {{ forms_obj.password }}
                  <span>{{ forms_obj.username.errors.0 }}</span>
          </div>
          <div>
              <label for="{{ forms_obj.sex.id_for_label }}">{{ forms_obj.sex.label }}</label>
              {{ forms_obj.sex }}
          </div>
          <div>
              <label for="{{ forms_obj.hobby.id_for_label }}">{{ forms_obj.hobby.label }}</label>
              {{ forms_obj.hobby }}
          </div>
          <div>
              <label for="{{ forms_obj.bothday.id_for_label }}">{{ forms_obj.bothday.label }}</label>
              {{ forms_obj.bothday }}
          </div>
      </form>
      </body>
      </html>
相關文章
相關標籤/搜索