• Django基础七之CBV装饰器和中间件


    Django基础七之CBV装饰器和中间件

    1. CBV加装饰器

    CBV加装饰器有三种方法,

    案例:要求登录(不管get请求还是post请求)后才可以访问

    HTML代码

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    
    <div>
        <p>Hello Index</p>
    </div>
    
    </body>
    </html>
    

    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>
        <form action="" method="post">
            <p>username:<input type="text" name="username"></p>
            <p>password:<input type="password" name="password"></p>
            <p><input type="submit" value="提交"></p>
        </form>
    
    </div>
    </body>
    </html>
    

    views.py

    # CBV加装饰器方法一:
    from django.shortcuts import render, HttpResponse, redirect
    
    from django.views import View
    
    from django.utils.decorators import method_decorator  # django提交加装饰器方法
    
    
    # Create your views here.
    
    # 装饰器
    def login_auth(func):
        def inner(request, *args, **kwargs):
            if request.session.get("is_login"):
                res = func(*args, **kwargs)
                return res
            else:
                return redirect('/login/')
    
        return inner
    
    
    class Index(View):
        # 方法一在每个需要验证的地方都加上装饰器
        @method_decorator(login_auth)
        def get(self, request):
            print("get 请求")
            return render(request, "index.html")
    	# 个需要验证的地方加上装饰器
    	@method_decorator(login_auth)
        def post(self, request):
            print("post 请求")
            return HttpResponse("post")
    
    
    def login(request):
        if request.method == "POST":
            name = request.POST.get("username")
            password = request.POST.get("password")
            if name == "hans" and password == "123":
                request.session['is_login'] = True
                print("登录成功")
    
        return render(request, "login.html")
    
    
    
    # CBV加装饰器方法二:
    from django.shortcuts import render, HttpResponse, redirect
    
    from django.views import View
    
    from django.utils.decorators import method_decorator
    
    
    # Create your views here.
    # 装饰器
    def login_auth(func):
        def inner(request, *args, **kwargs):
            if request.session.get("is_login"):
                res = func(*args, **kwargs)
                return res
            else:
                return redirect('/login/')
    
        return inner
    
    # 方法二 在类的上面加上,name为具体要加的函数
    @method_decorator(login_auth, name='post')
    @method_decorator(login_auth, name='get')
    class Index(View):
        def get(self, request):
            print("get 请求")
            return render(request, "index.html")
    
    
        def post(self, request):
            print("post 请求")
            return HttpResponse("post")
    
    
    def login(request):
        if request.method == "POST":
            name = request.POST.get("username")
            password = request.POST.get("password")
            if name == "hans" and password == "123":
                request.session['is_login'] = True
                print("登录成功")
    
        return render(request, "login.html")
    
    
    # CBV加装饰器方法三:
    
    from django.shortcuts import render, HttpResponse, redirect
    
    from django.views import View
    
    from django.utils.decorators import method_decorator
    
    
    # Create your views here.
    # 装饰器
    def login_auth(func):
        def inner(request, *args, **kwargs):
            if request.session.get("is_login"):
                res = func(*args, **kwargs)
                return res
            else:
                return redirect('/login/')
    
        return inner
    
    class Index(View):
        #方法三  使用dispatch给所有的方法添加装饰器
        @method_decorator(login_auth)
        def dispatch(self, request, *args, **kwargs):
            return super().dispatch(request, *args, **kwargs)
    
        def get(self, request):
            print("get 请求")
            return render(request, "index.html")
    
    
        def post(self, request):
            print("post 请求")
            return HttpResponse("post")
    
    
    def login(request):
        if request.method == "POST":
            name = request.POST.get("username")
            password = request.POST.get("password")
            if name == "hans" and password == "123":
                request.session['is_login'] = True
                print("登录成功")
    
        return render(request, "login.html")
    
    

    urls.py

    from django.contrib import admin
    from django.urls import path
    
    from wrapperMidd  import views
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('index/', views.Index.as_view()),
        path('login/', views.login),
    ]
    

    访问地址:http://127.0.0.1:8000/index

    get的请求使用POSTMAN工具

    2. Django中间件

    2.1 Django中间件介绍

    中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。

    每个中间件组件负责做一些特定的功能,Django中自带了七个中间件

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',   #  安全中间件,为请求/响应周期提供了若干安全增强功能。每一项都可以通过设置独立地启用或禁用。
        'django.contrib.sessions.middleware.SessionMiddleware', # 启用会话支持
        'django.middleware.common.CommonMiddleware', # “通用”中间件
        'django.middleware.csrf.CsrfViewMiddleware', # CSRF 保护中间件,通过在 POST 表单中添加隐藏的表单字段,并检查请求的正确值,增加对跨站点伪造请求的保护。
        'django.contrib.auth.middleware.AuthenticationMiddleware', # 验证中间件,将代表当前登录的用户的 user 属性添加到每个传入的 HttpRequest 对象中
        'django.contrib.messages.middleware.MessageMiddleware', # 消息中间件,启用基于 cookie 和会话的消息支持
        'django.middleware.clickjacking.XFrameOptionsMiddleware', # X-Frame-Options 中间件,简单的 通过 X-Frame-Options 头的点击劫持保护。
    ]
    

    中间件(Middleware)在整个Djangorequest/response处理机制中的角色如下所示:

    HttpRequest -> Middleware(request) -> View -> Middleware(response) -> HttpResponse

    中间件常用于权限校验、限制用户请求、打印日志、改变输出内容等多种应用场景.而且中间件对Django的输入或输出的改变是全局的。

    Django 中间件作用:

    • 修改请求,即传送到 view 中的 HttpRequest 对象。
    • 修改响应,即 view 返回的 HttpResponse 对象。

    中间件执行顺序:

    image

    2.2 自定义中间件

    中间件可以定义四个方法:

    • process_request(self,request)
    • process_view(self, request, view_func, view_args, view_kwargs)
    • process_exception(self, request, exception)
    • process_response(self, request, response)

    主要为process_requestprocess_response

    2.2.1 自定义中间件

    在应用目录下新建一个 py 文件,名字自定义。

    在应用目录下创建myMiddle.py
    myMiddle.py:
    
    from django.utils.deprecation import MiddlewareMixin
    
    class myMinddle(MiddlewareMixin):
        def process_request(self, request):  # 在视图之前执行
            print("这是自定义中间件 请求1")
    
        def process_response(self,request, response):  #在视图之后执行
            print("这是自定义中间件 响应1")
            return response
    
    

    把自定义的中间件注册到setting.pyMIDDLEWARE里面:

    setting.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',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'wrapperMidd.myMinddle.myMinddle',  # 自定义中间件
    ]
    

    测试:

    views.py:
        
    from django.shortcuts import render, HttpResponse, redirect
    def testMinddle(request):
        print("testMinddle")
        return HttpResponse("TEST")
    
    urls.py:
        
    from django.contrib import admin
    from django.urls import path
    
    from appName  import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('testMinddle/', views.testMinddle),
    ]
    
    # 访问:http://127.0.0.1:8000/testMinddle/
    # 结果:
    """
    这是自定义中间件 请求1
    testMinddle
    这是自定义中间件 响应1
    """
    

    增加两个自定义中间件,执行过程:

    myMiddle.py:
    
    from django.utils.deprecation import MiddlewareMixin
    
    class myMinddle(MiddlewareMixin):
        def process_request(self, request):
            print("这是自定义中间件 请求1")
    
        def process_response(self,request, response):
            print("这是自定义中间件 响应1")
            return response
    
    
    class myMinddle2(MiddlewareMixin):
        def process_request(self, request):
            print("这是自定义中间件 请求2")
    
        def process_response(self,request, response):
            print("这是自定义中间件 响应2")
            return response
    
    
    setting.py:
    MIDDLEWARE = [
        ......
        'wrapperMidd.myMinddle.myMinddle',
        'wrapperMidd.myMinddle.myMinddle2',
    ]
    
    # 访问:http://127.0.0.1:8000/testMinddle/
    # 结果
    """
    这是自定义中间件 请求1
    这是自定义中间件 请求2
    testMinddle
    这是自定义中间件 响应2
    这是自定义中间件 响应1
    """
    

    如果在第一个中间件直接返回,执行顺序如果:

    myMiddle.py:
    
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    
    class myMinddle(MiddlewareMixin):
        def process_request(self, request):
            print("这是自定义中间件 请求1")
            return HttpResponse("request")   """在这里直接返回"""
        def process_response(self,request, response):
            print("这是自定义中间件 响应1")
            return response
    
    
    class myMinddle2(MiddlewareMixin):
        def process_request(self, request):
            print("这是自定义中间件 请求2")
    
        def process_response(self,request, response):
            print("这是自定义中间件 响应2")
            return response
    
    # 访问:http://127.0.0.1:8000/testMinddle/
    # 结果:
    网页上显示:request
    后台显示:
    """
    这是自定义中间件 请求1
    这是自定义中间件 响应1
    """
    
    

    2.2.2 自定义中间件总结

    1. 自定义中间件先执行process_request再执行views.py里的视图函数,最后再执行process_response,而且process_response函数必须要返回 return response

    2. 如果有多个自定义中间件,则执行顺序按settings.py里自上而下执行,写在上面的先执行。执行顺序

      自定义中间件1 process_request--->自定义中间件2 process_request-->视图函数-->自定义中间件2 process_response -->自定义中间件1 process_response
      
    3. 如果自定义中间件的process_request里有return返回,而这个中间件还是在上面,则它会执行自己定义的process_requestprocess_response,则视图函数和其他的中间件都不执行

    4. 如果自定义中间件的process_request里有return返回,而这个中间件上面还有其他的中间件,则会自上而下执行,执行到自定义中间件的process_request后就会执行process_response,则视图函数和它下面的中间件都不执行

      MIDDLEWARE = [
          ...其他中间件...
          '自定义中间件1',
          '自定义中间件2',  #  自定义中间件2里使用return直接返回 
          '自定义中间件3',
      ]
      
      执行顺序:
      """
      其他中间件 process_request  --> 自定义中间件1 process_request --> 自定义中间件2 process_request --> 自定义中间件2 process_response --> 自定义中间件1 process_response -->其他中间件 process_response
      """
      视图函数和自定义中间件3是不执行的
      

    2.2.3 其他中间件函数

    process_view

    process_view在process_request之后,路由转发到视图,执行视图之前执行。
    
    process_view() 只在 Django 调用视图前被调用。它应该返回 None 或 HttpResponse 对象。如果它返回 None ,Django 将继续处理这个请求,执行任何其他的 process_view() ,然后执行相应的视图。如果它返回 HttpResponse 对象,Django 不会去影响调用相应的视图;它会将响应中间件应用到 HttpResponse 并返回结果。
    
    
    函数定义:
    process_view(request, view_func, view_args, view_kwargs)
    request 是一个 HttpRequest 对象。
    view_func 是一个 Django 将要使用的 Python 函数。(这是一个真实的函数对象,不是函数的名称);view_args 是一个用来传递给视图的位置参数列表,;
    view_kwargs 是一个用来传递给视图的关键字参数字典。
    view_args 和 view_kwargs 都不包含第一个视图参数 ( request )。
    

    process_exception

    视图执行中发生异常时执行。
    
    当视图引发异常时,Django 会调用 process_exception()。process_exception() 应该返回 None 或 HttpResponse 对象。如果它返回一个 HttpResponse 对象,模板响应和响应中间件将被应用且会将结果响应返回浏览器。否则,就会开始默认异常处理( default exception handling )。
    
    再次,中间件在响应阶段会按照相反的顺序运行,其中包括 process_exception 。如果异常中间件返回一个响应,那么中间件之上的中间件类的 process_exception 方法根本不会被调用。
    
    函数定义:
    process_exception(request, exception)
    request 是一个 HttpRequest 对象。 exception 是一个由视图函数引发的 Exception 对象。
    

    process_template_response

    视图函数刚执行完毕,process_response之前执行。
    
    
    process_template_response() 在视图被完全执行后调用,如果响应实例有 render() 方法,表明它是一个 TemplateResponse 或等效对象。
    
    它必须返回一个实现了 render 方法的响应对象。它可以通过改变``response.template_name`` 和 response.context_data 来改变给定的 response ,或者它可以创建和返回全新的 TemplateResponse 或等效对象。
    
    不需要显式地渲染响应——一旦所有模板中间件被调用,响应会被自动渲染。
    
    中间件会在响应阶段按照相反的顺序运行,其中包括 process_template_response() 。
    
    
    函数定义:
    process_template_response(request, response)
    
    request 是一个 HttpRequest 对象。
    response 是 TemplateResponse 对象(或者等效对象),它通过 Django 视图或中间件返回。
    
    
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    
    class myMinddle(MiddlewareMixin):
        def process_request(self, request):
            print("这是自定义中间件 请求1")
    
        def process_response(self,request, response):
            print("这是自定义中间件 响应1")
            return response
    
        def process_view(self,request, view_func, view_args, view_kwargs):
            print("视图函数之前执行")
    
        def process_exception(self,request,exception):
            print("处理视图函数")
            
    访问http://127.0.0.1:8000/testMinddle/
    结果:
    这是自定义中间件 请求1
    视图函数之前执行
    testMinddle
    这是自定义中间件 响应1
    
    
    # 视图函数出错示例:
    这是自定义中间件 请求1
    视图函数之前执行
    testMinddle
    处理视图函数错误
    这是自定义中间件 响应1
    

    2.3 新版本中间件写法

    官网上给的示例:

    class SimpleMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
            # 配置和初始化
    
        def __call__(self, request):
            # 在这里编写视图和后面的中间件被调用之前需要执行的代码,即process_request()
            response = self.get_response(request)
    
            # 在这里编写视图调用后需要执行的代码,即process_response()
    
            return response
    

    案例:

    使用官网上的写法不用继承 MiddlewareMixin
    
    class SimpleMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
            # One-time configuration and initialization.
    
        def __call__(self, request):
            # Code to be executed for each request before
            # the view (and later middleware) are called.
            print("这是自定义中间件 SimpleMiddleware的请求")
            response = self.get_response(request)
    
            # Code to be executed for each request/response after
            # the view is called.
            print("这是自定义中间件 SimpleMiddleware的响应")
    
            return response
    
    # 执行结果:
    这是自定义中间件 SimpleMiddleware的请求
    testMinddle
    这是自定义中间件 SimpleMiddleware的响应
    
    

    注意

    __init__(get_response)

    中间件必须接受 get_response 参数。还可以初始化中间件的一些全局状态。记住两个注意事项:

    • Django仅用 get_response 参数初始化您的中间件,因此不能定义 __init__() ,因为需要其他参数。
    • 与每次请求都会调用 __call__() 方法不同,当 Web 服务器启动后,__init__() 只被调用一次

    上面只定义了process_requestprocess_response 其中process_viewprocess_exception还是要写。

    class SimpleMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
           
        def __call__(self, request):
    
            print("这是自定义中间件 SimpleMiddleware的请求")
            response = self.get_response(request)
            
            print("这是自定义中间件 SimpleMiddleware的响应")
    
            return response
    
        def process_view(self,request, view_func, view_args, view_kwargs):
            print("视图函数之前执行")
    
        def process_exception(self,request,exception):
            print("处理视图函数错误")
    

    3.Csrf中间件

    使用Django框架使用django.middleware.csrf.CsrfViewMiddleware中间件,在前端页面提交操作的时候,会报错:

    Forbidden (403)
    CSRF verification failed. Request aborted.
    

    解决方法:

    如果使用form提交,则在前端页面里加入:
        {% csrf_token %}
    如:
    <div>
        <form action="" method="post">
         {% csrf_token %}
        <label>username: <input type="text" name="username"></label>
        <label>password:<input type="password" name="password"></label>
        <label><input type="submit" value="提交"></label>
        </form>
    </div>
    
    
    如果是Ajax提交:
    """一定要导入jquery"""
    <body>
    <div>
        <label>username: <input type="text" name="username" id="user"></label>
        <label>password:<input type="password" name="password" id="pwd"></label>
        <input type="button" value="提交" id="btn">
    
    </div>
    <script>
        $('#btn').click(function (){
            $.ajax({
                url: "",
                method: "post",
                data: {username: $('#user').val(), password: $('#pwd').val(), csrfmiddlewaretoken: '{{csrf_token}}'},
    
                success: function (data) {
                    console.log(data)
    
                }
            })
    
        })
    
    </script>
    </body>
    
    # 使用cookie:
    使用cookie 则要导入"""jquery.cookie.min.js"""
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
    
    const csrftoken = $.cookie('csrftoken');
    
    使用:
    <body>
    <div>
        <label>username: <input type="text" name="username" id="user"></label>
        <label>password:<input type="password" name="password" id="pwd"></label>
        <input type="button" value="提交" id="btn">
    
    </div>
    <script>
        $('#btn').click(function (){
            const csrftoken = $.cookie('csrftoken');
            $.ajax({
                url: "",
                headers:{'X-CSRFToken': csrftoken},  // 加请求头。
                method: "post",
                data: {username: $('#user').val(), password: $('#pwd').val()},
                success: function (data) {
                    console.log(data)
    
                }
            })
    
        })
    
    </script>
    </body>
    
    

    全局使用csrf局部函数使用,或全局不使用,局部函数使用csrf

    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    # 全局使用,局部不使用
    @csrf_exempt
    def xxx()
    
    # 全局不使用(禁用掉),局部使用
    @csrf_protect
    def yyy()
    
  • 相关阅读:
    Javaweb学生信息管理系统(Mysql+JSP+MVC+CSS)
    XFF漏洞利用([SWPUCTF 2021 新赛]Do_you_know_http)
    画流程图用什么软件好?安利这几款
    LeetCode·968.监控二叉树·贪心
    Android的权限管理系统工作原理详解
    [机缘参悟-77]:深度思考-职场中注意事项-管理者版
    MySQL----(五)索引、视图以及设计三范式
    【创建型模式】抽象工厂模式
    【LLM】基于LLM的agent应用(上)
    ADC架构种类说明_笔记
  • 原文地址:https://www.cnblogs.com/hans-python/p/16037470.html