19-Django中间件

Django中间件

中间件(middleware)允许您在一个浏览器的请求在到达Django视图之前处理它,以及在视图返回的响应到达浏览器之前处理这个响应。

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

HttpRequest -> Middleware -> View -> Middleware -> HttpResponse

中间件常用于权限校验、限制用户请求、打印日志、改变输出内容等多种应用场景,比如:

- 禁止特定IP地址的用户或未登录的用户访问我们的View视图函数
- 对同一IP地址单位时间内发送的请求数量做出限制
- 在View视图函数执行前传递额外的变量或参数
- 在View视图函数执行前或执行后把特定信息打印到log日志
- 在View视图函数执行后对response数据进行修改后返回给用户

注意:装饰器也经常用于用户权限校验。但与装饰器不同,中间件对Django的输入或输出的改变是全局的。比如`@login_required`装饰器仅作用于单个视图函数。如果你希望实现全站只有登录用户才能访问,编写一个中间件是一个更好的解决方案。
  1. Django中间件是Django的门户
    1. 请求来的时候需要先经过中间件才能到达真正的Django后端
    2. 响应走的时候最后也需要经过中间件才能发送出去  
  2. Django自带七个中间件
# 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",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

"""
每个中间件的功能如下,建议都保留:

SecurityMiddleware:为request/response提供了几种安全改进;
SessionMiddleware:开启session会话支持;
CommonMiddleware:基于APPEND_SLASH和PREPEND_WWW的设置来重写URL,如果APPEND_SLASH设为True,并且初始URL 没有以斜线结尾以及在URLconf 中没找到对应定义,这时形成一个斜线结尾的新URL;
CsrfViewMiddleware:添加跨站点请求伪造的保护,通过向POST表单添加一个隐藏的表单字段,并检查请求中是否有正确的值;
AuthenticationMiddleware:在视图函数执行前向每个接收到的user对象添加HttpRequest属性,表示当前登录的用户,无它用不了request.user。
MessageMiddleware:开启基于Cookie和会话的消息支持
XFrameOptionsMiddleware:对点击劫持的保护

除此以外, Django还提供了压缩网站内容的GZipMiddleware,根据用户请求语言返回不同内容的LocaleMiddleware和给GET请求附加条件的ConditionalGetMiddleware。这些中间件都是可选的。
"""

自定义中间件

1、在项目名或者应用名下创建一个任意名称的文件夹
2、在该文件夹内创建一个任意名称的py文件
3、在该py文件内需要书写类(这个类必须继承MiddlewareMixin),然后在这个类里面就可以自定义五个方法了,五个方法并不是全部都要书写,用几个写几个
4、需要将类的路径以字符串的形式注册到配置文件中才能生效

image-20240316223936367

  1. Django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法
必须掌握
process_request  process_response

了解即可
process_view  process_template_response  process_exception
# 自定义的中间件
from django.utils.deprecation import MiddlewareMixin


# 这个就是自定义的中间件
class MyMiddleWare(MiddlewareMixin):
    # 要重写 process_request 方法
    # process_request
    # 1、请求来的时候需要经过每一个中间件里面的process_request方法,经过的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
    # 2、如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件
    # 3、如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行,而是直接原路返回(校验失败不允许访问...)
    # process_request方法就是用来做全局相关的所有限制功能
    # rpocess_request不需要return django内部自己帮我们做了return了
    def process_request(self, request):
        print('自定义中间件的请求')

    # 重写process_response 方法
    # process_response
    # 1、响应走的时候需要经过每一个中间件里面的process_response方法,该方法有两个额外的参数request,response
    #     response就是Django后端返回给浏览器的内容
    # 2、该方法必须返回一个HttpResponse对象
    #     默认返回的就是形参response
    #     你也可以自己返回自己
    # 3、顺序是按照配置文件中注册了的中间件从下往上依次经过,如果没有定义的话 直接跳过执行下一个
    # 4、如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候会直接走同级别的process_reponse返回
    def process_response(self, request, response):
        print("自定义中间件的响应")
        # 这里一定要加response 不然会报错
        return response

注意:

  1. 自定义的中间件 request 方法不要return 因为返回值中间件不再往下执行,导致 http 请求到达不了视图层,因为 request 在视图之前执行!
  2. response 必须要 return因为要继续传递到下一个中间件。
# 自己定义的中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


# 这个就是自定义的中间件
class MyMiddleWare01(MiddlewareMixin):
    def process_request(self, request):
        print('自定义中间件的请求')
        return HttpResponse('不好意思,你就到这里了')
    
        def process_response(self, request, response):
        return response
   
# 视图层
def test(request):
    return HttpResponse('测试中间件')

image-20240316230345565

# callback 回调函数  callback_args 可变长位置参数  callback_kwargs 可变长关键字参数
# 等视图函数全部执行完毕之后会触发这个的执行
def process_view(self, request, callback, callback_args, callback_kwargs):
    print(f'函数名称 {callback.__name__}')
    print(f'args 【{callback_args}】')
    print(f'kwargs 【{callback_kwargs}】')

"""
函数名称 test
args 【()】
kwargs 【{}】
"""
# process_exception 默认不执行,当视图函数出错了才会执行,三个参数
# self, request, exception
# 1. 执行完所有的request方法
# 2. 执行完所有的process_view方法
# 3. 如果视图函数出错,执行process_exception(最终response process_exception的 return值),执行顺序是MIDDLEWARE 从后往前执行
# 	- 如果process_exception 方法有了返回值就不在执行其它中间件的 process_exception,直接执行response方法响应(process_exception中的response)。
# 4. 执行所有的 response 方法
# 5. 最后返回process_exception 的返回值
def process_exception(self, request, exception):
    print(exception)
    return HttpResponse(f'对不起,您访问的资源已丢失!{ exception}')

image-20240316232413594

# process_template_response
# 默认不执行,需要在视图函数返回render的时候才会执行,常规的return render没有效果,按照下面的写视图函数才可以

# 路由
path('test/<str:msg>/', view.test, name='test')


# 视图
class Foo(object):
    def __init__(self, request, msg):
        self.request = request
        self.msg = msg

    def render(self):
        result = self.msg
        return HttpResponse(result)

def test(request, msg):
    print(msg)
    obj = Foo(request, msg)
    return obj



# 自定义中间件
# process_template_response 默认不执行,只有对象render的时候才会执行
# 一样需要return response才会触发
def process_template_response(self, request, response):
    print('process_template_response')
    return response

image-20240317001401007

简单总结:

场景

  1. process_request 可以实现指定url的拦截
  2. process_exception 可以实现全局异常处理器

执行顺序

  1. process_request
  2. process_view
  3. 然后才去执行视图层的代码
  4. 如果有错误执行process_exception
  5. 如果没有错误执行process_template_response
  6. 最后执行process_response

django如何阻止CSRF的使用

#  方案1 form表单中添加 {% csrf_token %}
#  方案2 页面任何地方添加 {% csrf_token %} 然后发送Ajax的时候带上这个 
 data:{csrfmiddlewaretoken:'{{ csrf_token }}'},
# 方案3 settings里面注销掉 "django.middleware.csrf.CsrfViewMiddleware"
# 方案4 使用 django-csrf_exempt

from django.views.decorators.csrf import csrf_exemp

@csrf_exemp
def demo(request):
    if request.method == 'POST':
        ...
 # 使用此方法前端一定要加上 {% csrf_token %}

通过中间件获取对方的ip地址和user_agent

ip_addr = request.META.get('REMOTE_ADDR')
user_agent = request.META.get('HTTP_USER_AGENT')
posted @ 2024-03-23 00:53  小满三岁啦  阅读(1)  评论(0编辑  收藏  举报