django中间件

Posted tornadoes-destroy-parking-lots

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django中间件相关的知识,希望对你有一定的参考价值。

简单介绍:

django自带七个中间件,每个中间件都有各自对应的功能,并且django还支持程序员自定义中间件

在用django开发项目的项目的时候,只要是涉及到全局相关的功能都可以使用中间件方便的完成

# django中间件是django的门户
1.请求来的时候需要先经过中间件才能到达真正的django后端
2.响应走的时候最后也需要经过中间件才能发送出去

django自带七个中间件

Django中间件:默认自带七个如下

技术图片
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,
]
技术图片

解释,这其中的字符串元素是路径名,利用字符串导模块(利用importlib模块)

from django.middleware.security import SecurityMiddleware

查看几个中间件源码

技术图片
class SessionMiddleware(MiddlewareMixin):
    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)
    def process_response(self, request, response):
        return response
      
class CsrfViewMiddleware(MiddlewareMixin):
      def process_request(self, request):
        csrf_token = self._get_token(request)
        if csrf_token is not None:
            # Use same token next time.
            request.META[CSRF_COOKIE] = csrf_token
    def process_view(self, request, callback, callback_args, callback_kwargs):
        return self._accept(request)

    def process_response(self, request, response):
        return response
      
class AuthenticationMiddleware(MiddlewareMixin):
    def process_request(self, request):
        request.user = SimpleLazyObject(lambda: get_user(request))
技术图片

django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法

技术图片
# 1.必须掌握
    process_request

    process_response
# 2.了解即可
    process_view

    process_template_response

    process_exception
技术图片

自定义中间件

技术图片
# 1.在项目名或者应用名下创建一个任意名称的文件夹

# 2.在该文件夹内创建一个任意名称的py文件

# 3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin)
from django.utils.deprecation import MiddlewareMixin
然后在这个类里面就可以自定义五个方法了
(这五个方法并不是全部都需要书写,用几个写几个)

# 4.需要将类的路径以字符串的形式注册到配置文件中才能生效
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,
你自己写的中间件的路径1,
你自己写的中间件的路径2,
你自己写的中间件的路径3,
]
技术图片

案例

1、在应用名下创建一个名为mymiddleware的文件夹

2、在该文件夹内创建一个mydd.py文件

技术图片

 3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin)

mydd.py

技术图片
from django.utils.deprecation import MiddlewareMixin

class MyMiddleware1(MiddlewareMixin):
    def process_request(self,request):
        print("我是第一个自定义中间件的process_request方法")

    def process_response(self,request,response):
        """
        :param request:
        :param response: 就是django后端返回给浏览器的内容
        :return:必须返回response
        """
        print("我是第一个自定义中间件的process_response方法")
        return response

class MyMiddleware2(MiddlewareMixin):
    def process_request(self,request):
        print("我是第二个自定义中间件的request方法")

    def process_response(self,request,response):
        """
        :param request:
        :param response: 就是django后端返回给浏览器的内容
        :return:response
        """
        print("我是第二个自定义中间件的process_response方法")
        return response
技术图片

4、需要将类的路径以字符串的形式注册到配置文件中才能生效

技术图片
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.mymiddleware.mydd.MyMiddleware1,
    app01.mymiddleware.mydd.MyMiddleware2,
]
技术图片

urls.py

技术图片
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r^admin/, admin.site.urls),
    url(r^index/, views.index),
]
技术图片

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    print("我是视图函数index")
    return HttpResponse("index")

浏览器访问

技术图片

 查看后端输出

 技术图片

总结

技术图片
"""
1.必须掌握
        process_request 
            1.请求来的时候需要经过每一个中间件里面的process_request方法
            结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
            2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件
            3.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行
            而是直接原路返回(就相当于校验失败不允许访问...)
            process_request方法就是用来做全局相关的所有限制功能
            
        process_response
            1.响应走的时候需要结果每一个中间件里面的process_response方法
            该方法有两个额外的参数request,response
            2.该方法必须返回一个HttpResponse对象
                1.默认返回的就是形参response
                  return response
                2.你也可以自己返回自己的(狸猫换太子)
                  return HttpResponse("Hello world!")
            3.顺序是按照配置文件中注册了的中间件从下往上依次经过
                如果你没有定义的话 直接跳过执行下一个
        
        研究如果在第一个自定义中间件process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况
        是其他情况
            结论:如果第一个自定义中间件process_request方法就已经返回了HttpResponse对象,那么响应走的时候会直接走同级别的的第一个自定义中间件中的process_reponse返回
        
        flask框架也有一个中间件但是它的规律
            只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法
            
            
2.了解即可
        process_view
            路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该放法
            顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
            
        process_template_response
            返回的HttpResponse对象有render属性的时候才会触发
            顺序是按照配置文件中注册了的中间件从下往上依次经过
            # mydd.py
            def process_template_response(self,request,response):
                print(‘我是第一个自定义中间件里面的process_template_response‘)
                return response
            # views.py
                def index(request):
                    print(‘我是视图函数index‘)
                    obj = HttpResponse(‘index‘)

                    def render():
                        print(‘内部的render‘)
                        return HttpResponse("O98K")
                    obj.render = render
                    return obj
process_exception 当视图函数中出现异常的情况下触发 顺序是按照配置文件中注册了的中间件从下往上依次经过
"""
技术图片

csrf跨站请求伪造

技术图片
"""
钓鱼网站
    我搭建一个跟正规网站一模一样的界面(中国银行)
    用户不小心进入到了我们的网站,用户给某个人打钱
    打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了
    但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户

大学英语四六级
    考之前需要学生自己网站登陆缴费

内部本质
    我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框
    然后我们在内部隐藏一个已经写好name和value的input框

如何规避上述问题
    csrf跨站请求伪造校验
        网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识
        当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden),如果成功则正常执行    
"""
技术图片

钓鱼网站案例:

正规网站:

urls.py

技术图片
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r^admin/, admin.site.urls),
    # 转账
    url(r^transfer/, views.transfer),

]
urls.py

views.py

技术图片
from django.shortcuts import render,HttpResponse

# Create your views here.
def transfer(request):
    if request.method == "POST":
        username = request.POST.get("username")
        target_user = request.POST.get("target_user")
        money = request.POST.get("money")
        print("%s 给 %s 转了%s元" %(username,target_user,money))
    return render(request,"transfer.html")
views.py

transfer.html

技术图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>我是正儿八经的网站</h1>
<form action="" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>

</body>
</html>
transfer.html

钓鱼网站:

urls.py

技术图片
from django.conf.urls import url
from django.contrib import admin
from app01 import  views

urlpatterns = [
    url(r^admin/, admin.site.urls),
    url(r^transfer/, views.transfer),
]
urls.py

views.py

技术图片
from django.shortcuts import render

# Create your views here.
def transfer(request):

    return render(request,"transfer.html")
views.py

transfer.html

技术图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>我是钓鱼网站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text"></p>
    <input type="hidden" name="target_user" value="jason">
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>

</body>
</html>
transfer.html

如何符合csrf校验

form表单如何符合校验,在form表单中任意位置添加{% csrf_token %}

技术图片
<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
技术图片

技术图片

此时,上面钓鱼网站例子中钓鱼网站提交到正规的网站时,就会403

ajax如何符合校验

方式一:利用标签查找获取页面上的随机字符串,data中添加一个键csrfmiddlewaretoken(名字必须叫这个),值为拿到的随机字符串

技术图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>我是正儿八经的网站</h1>
<form action="/csrf/" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
<button id="d1">ajax请求</button>

<script>
    $("#d1").click(function () {
        $.ajax({
            url:"",
            type:"post",
            // 方式一:利用标签查找获取页面上的随机字符串
            data:{"username":jason,csrfmiddlewaretoken:$([name=csrfmiddlewaretoken]).val()},
            success:function () {
                
            }
        })
    })
</script>
</body>
</html>
技术图片

方式二:利用模版语法提供的快捷书写

技术图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>我是正儿八经的网站</h1>
<form action="/csrf/" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
<button id="d1">ajax请求</button>

<script>
    $("#d1").click(function () {
        $.ajax({
            url:"",
            type:"post",
            // 第二种 利用模版语法提供的快捷书写
            data:{"username":jason,csrfmiddlewaretoken:{{ csrf_token }}},
            success:function () {
            }
        })
    })
</script>
</body>
</html>
技术图片

方式三:通用方式直接拷贝js代码并应用到自己的html页面上即可

配置静态文件

技术图片

 mysteup.js

技术图片
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‘);

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);
    }
  }
});
mysteup.js

要引用的html文件

技术图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>我是正儿八经的网站</h1>
<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
<button id="d1">ajax请求</button>

<!--动态引入静态文件-->
{% load static %}
<script src="{% static "js/mysteup.js" %}/"></script>
<script>
    $("#d1").click(function () {
        $.ajax({
            url:"",
            type:"post",
            data:{"username":jason},
            success:function () {
            }
        })
    })
</script>

</body>
</html>
技术图片

 csrf相关装饰器

"""
1.网站整体都不校验csrf,就单单几个视图函数需要校验
2.网站整体都校验csrf,就单单几个视图函数不校验
"""

两个装饰器:

技术图片
from django.views.decorators.csrf import csrf_protect,csrf_exempt

"""
csrf_protect  需要校验
    针对csrf_protect符合我们之前所学的装饰器的三种玩法
csrf_exempt   忽视校验
    针对csrf_exempt只能给dispatch方法加才有效
"""
技术图片

FBV上使用:

技术图片
from django.views.decorators.csrf import csrf_protect,csrf_exempt

"""
csrf_protect  需要校验
    针对csrf_protect符合我们之前所学的装饰器的三种玩法
csrf_exempt   忽视校验
    针对csrf_exempt只能给dispatch方法加才有效
"""
# @csrf_exempt
# @csrf_protect
def transfer(request):
    if request.method == POST:
        username = request.POST.get(username)
        target_user = request.POST.get(target_user)
        money = request.POST.get(money)
        print(%s给%s转了%s元%(username,target_user,money))
    return render(request,transfer.html)
技术图片

CBV上使用

技术图片
from django.views.decorators.csrf import csrf_protect,csrf_exempt
from django.utils.decorators import method_decorator
from django.views import View
"""
csrf_protect  需要校验
    针对csrf_protect符合我们之前所学的装饰器的三种玩法
csrf_exempt   忽视校验
    针对csrf_exempt只能给dispatch方法加才有效
"""

# @method_decorator(csrf_protect,name=‘post‘)  # 针对csrf_protect 第二种方式可以
# @method_decorator(csrf_exempt,name=‘post‘)  # 针对csrf_exempt 第二种方式不可以
@method_decorator(csrf_exempt,name=dispatch)
class MyCsrfToken(View):
    # @method_decorator(csrf_protect)  # 针对csrf_protect 第三种方式可以
    # @method_decorator(csrf_exempt)  # 针对csrf_exempt 第三种方式可以
    def dispatch(self, request, *args, **kwargs):
        return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)

    def get(self,request):
        return HttpResponse(get)

    # @method_decorator(csrf_protect)  # 针对csrf_protect 第一种方式可以
    # @method_decorator(csrf_exempt)  # 针对csrf_exempt 第一种方式不可以
    def post(self,request):
        return HttpResponse(post)
技术图片

补充知识点

# 模块:importlib
import importlib
res = myfile.b
ret = importlib.import_module(res)  # from myfile import b
# 该方法最小只能到py文件名
print(ret)

根据中间件的settings推导出重要思想

在settings.py文件中配置功能模块文件夹字符串,想用的时候打开,不想用直接注释掉(全局就不可以用这个功能)

案例发送通知(通过QQ、微信。。。)

正常思路:比较麻烦,如果像去掉一个功能很费劲

notify.py

技术图片
def wechat(content):
    print("微信通知:%s" %content)

def qq(content):
    print("QQ通知:%s" %content)

def email(content):
    print("邮箱通知:%s" %content)
技术图片

start.py

技术图片
from notify import *

def send_all(content):
    wechat(content)
    qq(content)
    email(content)

if __name__ == __main__:
    send_all("这是个重要思想")
技术图片

使用中间件中的通过字符串导入模块方法的思想

目录结构:

技术图片
─test
    │
    ├─notify
    │  ├─__init__.py
    │  │
    │  ├─email.py
    │  │
    │  ├─qq.py
    │  │
    │  └─wechat.py
    │
    ├─settings.py
    │
    └─start.py
技术图片

notify文件夹

技术图片
class Email(object):
    def __init__(self):
        pass
    def send(self,content):
        print("邮箱通知:%s" %content)
email.py
技术图片
class QQ(object):
    def __init__(self):
        pass
    def send(self,content):
        print("QQ通知:%s" %content)
qq.py
技术图片
class Wechat(object):
    def __init__(self):
        pass
    def send(self,content):
        print("微信通知:%s" %content)
wechat.py
技术图片
import settings
import importlib

def send_all(content):
    for path_str in settings.NOTIFY_LIST:  # path_str   ‘notify.email.Email‘
        module_path,class_name = path_str.rsplit(".",1)
        # module_path = ‘notify.email‘
        # 利用字符串导入模块
        module = importlib.import_module(module_path)     # from notify import email
        # 利用反射获取类名
        cls = getattr(module,class_name)   # 拿到真正类方法的内存地址
        # 生成类的对象
        obj = cls()
        # 利用鸭子类型直接调用send方法
        obj.send(content)
__init__.py

settings.py

NOTIFY_LIST = [
    notify.email.Email,
    notify.wechat.Wechat,
    notify.qq.QQ,
]

start.py

import notify

notify.send_all(这是个非非常重要的思想)

 

以上是关于django中间件的主要内容,如果未能解决你的问题,请参考以下文章

django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE的解决办法(转)(代码片段

django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE的解决办法(转)(代码片段

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

django-中间件

django之设置中间件模板

Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]