第06章 上下文处理器与中间件

什么是上下文处理器?

上下文处理器是可以返回一些数据,在全局模板中都可以使用。

比如登录后的用户信息,在很多页面中都需要使用,那么我们可以放在上下文处理器中,就没有必要在每个视图函数中都返回这个对象。

功能:用于模板中,返回一些数据。

django中,在settings.TEMPLATES.OPTIONS.context_processors中,有很多内置的上下文处理器。这些上下文处理器的作用如下:

  1. django.template.context_processors.debug: 增加一个debug和sql_queries变量。在模板中就可以通过他来查看到一些数据库查询。
  2. django.template.context_processors.request:增加一个request变量。这个request变更也就是在视图函数的第一个参数。
  3. django.template.context_processors.auth:django有内置的用户系统,这个上下文处理器会增加一个user对象。
  4. django.template.context_processors.messages:增加五个messages变量。

自定义上下文处理器:

  1. 定义上下文处理器
  2. 注册上下文处理器
  1. # 1.定义上下文管理器
  2. from .models import User
  3. def front_user(request):
  4. user_id = request.session.get('user_id)
  5. context = {}
  6. if user_id:
  7. try:
  8. user = User.object.get(pk=user_id)
  9. context['front_user'] = user
  10. except:
  11. pass
  12. return context
  13. # 2. 注册上下文处理
  14. # setttings.py
  15. TEMPLATES = [{
  16. ...,
  17. 'context_processors': [
  18. ...,
  19. 'front.context_precessors.front_user'
  20. ]
  21. }]

django自带上下文处理器

  1. {
  2. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  3. 'DIRS': [],
  4. 'APP_DIRS': True,
  5. 'OPTIONS': {
  6. 'context_processors': [
  7. 'django.template.context_processors.debug',
  8. 'django.template.context_processors.request',
  9. 'django.contrib.auth.context_processors.auth',
  10. 'django.contrib.messages.context_processors.messages',
  11. ],
  12. },
  13. },
  14. ]

中间件

什么是中间件 middleware

middleware, 意即过滤器,使用的是装饰器技术

中间件用途

中间件是在request和response处理过程中的一个插件。比如在request到达视图函数之前,我们可以使用中间件来做一些相关的事情,比如:

  • 可以判断当前这个用户有没有登录,如果登录了,就绑定一个user对象到request上。
  • 也可以在response到达浏览器之前,做一些相关的操作,
  • 比如想要统一在response上设置一些cookie信息等。

自定义中间件

中间件的位置没有规定。只要放到项目当中即可。一般分为两种情况,

  • 如果中间件是属于某个app的,那么可以在这个app下面创建一个python文件来存放这个中间件,
  • 也可以专门创建的一个python包,用来存放本藏在的所有中间件。

创建中间件有两种方式,

  1. 使用函数
  2. 使用类

使用函数的中间件

  1. def simple_middleware(get_response):
  2. # 这个中间件初始化的代码
  3. def middleware(request):
  4. # request 到达view之前的执行代码
  5. response = get_response(request)
  6. # response 到达浏览器之前的执行代码
  7. return response
  8. return middleware

使用类的中间件

  1. class FrontUserMiddleware:
  2. def __init__(self, get_response):
  3. # 这个中间件初始化的代码
  4. self.get_response = get_response
  5. def __call__(self, request):
  6. print("request 到达view之前的执行代码")
  7. user_id = request.session.get('user_id')
  8. if user_id:
  9. try:
  10. user = User.objects.get(pk=user_id)
  11. request.front_user = user
  12. except:
  13. request.front_user = None
  14. else:
  15. request.front_user = None
  16. response = self.get_response(request)
  17. # response 到达浏览器之前的执行代码
  18. return response

项目中的中间件

  1. from django.conf import settings
  2. from ipware.ip import get_ip
  3. from server_side_piwik.tasks import record_analytic
  4. class PiwikMiddleware(object):
  5. """ Record every request to piwik """
  6. def __init__(self, get_response):
  7. self.get_response = get_response
  8. def __call__(self, request):
  9. response = self.get_response(request)
  10. SITE_ID = getattr(settings, 'PIWIK_SITE_ID', None)
  11. if SITE_ID:
  12. ip = get_ip(request)
  13. keys_to_serialize = [
  14. 'HTTP_USER_AGENT',
  15. 'REMOTE_ADDR',
  16. 'HTTP_REFERER',
  17. 'HTTP_ACCEPT_LANGUAGE',
  18. 'SERVER_NAME',
  19. 'PATH_INFO',
  20. 'QUERY_STRING',
  21. ]
  22. data = {
  23. 'HTTPS': request.is_secure()
  24. }
  25. for key in keys_to_serialize:
  26. if key in request.META:
  27. data[key] = request.META[key]
  28. user_id = None
  29. if getattr(settings, 'PIWIK_TRACK_USER_ID', None) and request.user and request.user.pk:
  30. user_id = request.user.pk
  31. record_analytic.delay(data, ip, user_id)
  32. return response

内置中间件

  1. django.middleware.common.CommonMiddleware:通用中间件,作用如下:
    • 限制 settings.DISALLOWED_USER_AGENTS 中指定的请求来访问本网站(限制蜘蛛)
  1. import re
  2. DISALLOWED_USER_AGENTS = [
  3. re.compile(r'^\s$|^$'), # 所有空白符或者空字符
  4. re.compile(r'.*PhantomJS.*') # PhantomJS
  5. ]
  • 给url添加最后的斜杠
  1. django.middleware.gzip.GZipMiddleware:response响应数据压缩(response>=200字符才会压缩)
  2. django.contrib.messages.middleware.MessageMiddleware:消息处理相关的中间件
  3. django.middle.security.SecurityMiddleware:安全处理,如设置XSS防御的请求头,做http转https等
  4. django.contrib.sessions.middleware.SessionMiddleware:给request添加一个处理好的session对象
  5. django.contrib.auth.middleware.AuthenticationMiddleware:给request添加一个user对象
  6. django.middleware.csrf.CsrfViewMiddleware:CSRF保护
  7. django.middleware.clickjacking.XFrameOptionsMiddleware:做clickjacking保护
  8. 缓存中间件:用来缓存一些页面
    • django.middleware.cache.UpdateCacheMiddleware
    • django.middleware.cache.FetchFormCacheMiddleware

clickjacking: 指攻击者在自己的病毒网站上添加诱惑性的按钮,使用iframe技术将受攻击网站(比如银行)加载到自己的网站上去,并将其设置为透明;诱惑用户点击执行受攻击的网站操作。

中间件放置顺序

  1. Securitywiddleware:应该放到最前面。因为这个中间件并不需要依赖任何其他的中间件。如果你的网站同时支持http协议和https协议,并且你想让用户在使用http协议的时候重定向到https协议,那么就没有必要让他执行下面一大串中间件再重定向,这样效率更高。
  2. UpdateCacheMiddleware:应该在SessionMiddleware,GZipMiddleware,LocaleMiddleware之前。
  3. GZipMiddleware。
  4. ConditionalGetMiddleware。
  5. SessionMiddleware。
  6. LocaleMiddleware。
  7. CommonMiddleware。
  8. CsrfViewliddleware。
  9. AuthenticationMiddleware。
  10. MessageMiddleware。
  11. FetchFromCacheMiddleware。
  12. FlatpageFallbackMiddleware。
  13. RedirectFallbackMiddleware。

信号

Django 包含一个“信号调试程序”,它有助于在框架中的其他位置发生操作时通知分离的其他部分程序。也就是说:信号允许某些发送者通知一组接收器已经发生了某些动作。当许多代码可能对同一事件感兴趣时,它们特别有用。

django 内置了一组信号,让用户代码可以通过django自己通知某些操作。包括

信号 说明
django.db.models.signals.pre_save
django.db.models.signals.post_save 模型调用 save()
之前/之后发送
django.db.models.signals.pre_delete
django.db.models.signals.post_delete 调用模型的models.delete()
或者 queryset.delete()
方法之前/之后发送
django.core.signals.request_started
django.core.signals.request_finished django启动/完成http请求时发送
django.db.models.signals.m2m_changed MangToMangField更改模型时发送

文档地址:

https://docs.djangoproject.com/zh-hans/2.0/topics/signals/

https://docs.djangoproject.com/zh-hans/2.0/ref/signals/

监听信号

使用 Signal.conect()注册接收者函数,接收一个信号;当信号发生时,会调用接收者函数。

  1. Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)
  2. parameters:
  3. receiver - 回调函数连接此信号
  4. sender - 指定发送者
  5. weak - Django在默认情况下将信号处理程序存储为弱引用。因此,如果您的接收器是一个本地函数,那么它可能是垃圾收集的。为了防止这种情况发生,当您调用signal.connect()方法时,传递 weak=False
  6. dispatch_uid - 在可能发送重复信号的情况下,信号接收器的唯一标识符。有关更多信息,请参见防止重复信号 https://docs.djangoproject.com/zh-hans/2.0/topics/signals/#preventing-duplicate-signals

监听信号方法:


定义接收器


连接接收器


连接到特定发送者发送的信号


防止重复信号

定义和发送信号

定义信号

发送信号

断开信号