第06章 上下文处理器与中间件
什么是上下文处理器?
上下文处理器是可以返回一些数据,在全局模板中都可以使用。
比如登录后的用户信息,在很多页面中都需要使用,那么我们可以放在上下文处理器中,就没有必要在每个视图函数中都返回这个对象。
功能:用于模板中,返回一些数据。
django中,在settings.TEMPLATES.OPTIONS.context_processors中,有很多内置的上下文处理器。这些上下文处理器的作用如下:
django.template.context_processors.debug: 增加一个debug和sql_queries变量。在模板中就可以通过他来查看到一些数据库查询。django.template.context_processors.request:增加一个request变量。这个request变更也就是在视图函数的第一个参数。django.template.context_processors.auth:django有内置的用户系统,这个上下文处理器会增加一个user对象。django.template.context_processors.messages:增加五个messages变量。
自定义上下文处理器:
- 定义上下文处理器
- 注册上下文处理器
# 1.定义上下文管理器from .models import Userdef front_user(request):user_id = request.session.get('user_id)context = {}if user_id:try:user = User.object.get(pk=user_id)context['front_user'] = userexcept:passreturn context# 2. 注册上下文处理# setttings.pyTEMPLATES = [{...,'context_processors': [...,'front.context_precessors.front_user']}]
django自带上下文处理器
{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},]
中间件
什么是中间件 middleware
middleware, 意即过滤器,使用的是装饰器技术
中间件用途
中间件是在request和response处理过程中的一个插件。比如在request到达视图函数之前,我们可以使用中间件来做一些相关的事情,比如:
- 可以判断当前这个用户有没有登录,如果登录了,就绑定一个user对象到request上。
- 也可以在response到达浏览器之前,做一些相关的操作,
- 比如想要统一在response上设置一些cookie信息等。
自定义中间件
中间件的位置没有规定。只要放到项目当中即可。一般分为两种情况,
- 如果中间件是属于某个app的,那么可以在这个app下面创建一个python文件来存放这个中间件,
- 也可以专门创建的一个python包,用来存放本藏在的所有中间件。
创建中间件有两种方式,
- 使用函数
- 使用类
使用函数的中间件
def simple_middleware(get_response):# 这个中间件初始化的代码def middleware(request):# request 到达view之前的执行代码response = get_response(request)# response 到达浏览器之前的执行代码return responsereturn middleware
使用类的中间件
class FrontUserMiddleware:def __init__(self, get_response):# 这个中间件初始化的代码self.get_response = get_responsedef __call__(self, request):print("request 到达view之前的执行代码")user_id = request.session.get('user_id')if user_id:try:user = User.objects.get(pk=user_id)request.front_user = userexcept:request.front_user = Noneelse:request.front_user = Noneresponse = self.get_response(request)# response 到达浏览器之前的执行代码return response
项目中的中间件
from django.conf import settingsfrom ipware.ip import get_ipfrom server_side_piwik.tasks import record_analyticclass PiwikMiddleware(object):""" Record every request to piwik """def __init__(self, get_response):self.get_response = get_responsedef __call__(self, request):response = self.get_response(request)SITE_ID = getattr(settings, 'PIWIK_SITE_ID', None)if SITE_ID:ip = get_ip(request)keys_to_serialize = ['HTTP_USER_AGENT','REMOTE_ADDR','HTTP_REFERER','HTTP_ACCEPT_LANGUAGE','SERVER_NAME','PATH_INFO','QUERY_STRING',]data = {'HTTPS': request.is_secure()}for key in keys_to_serialize:if key in request.META:data[key] = request.META[key]user_id = Noneif getattr(settings, 'PIWIK_TRACK_USER_ID', None) and request.user and request.user.pk:user_id = request.user.pkrecord_analytic.delay(data, ip, user_id)return response
内置中间件
- django.middleware.common.CommonMiddleware:通用中间件,作用如下:
- 限制 settings.DISALLOWED_USER_AGENTS 中指定的请求来访问本网站(限制蜘蛛)
import reDISALLOWED_USER_AGENTS = [re.compile(r'^\s$|^$'), # 所有空白符或者空字符re.compile(r'.*PhantomJS.*') # PhantomJS]
- 给url添加最后的斜杠
- django.middleware.gzip.GZipMiddleware:response响应数据压缩(response>=200字符才会压缩)
- django.contrib.messages.middleware.MessageMiddleware:消息处理相关的中间件
- django.middle.security.SecurityMiddleware:安全处理,如设置XSS防御的请求头,做http转https等
- django.contrib.sessions.middleware.SessionMiddleware:给request添加一个处理好的session对象
- django.contrib.auth.middleware.AuthenticationMiddleware:给request添加一个user对象
- django.middleware.csrf.CsrfViewMiddleware:CSRF保护
- django.middleware.clickjacking.XFrameOptionsMiddleware:做clickjacking保护
- 缓存中间件:用来缓存一些页面
- django.middleware.cache.UpdateCacheMiddleware
- django.middleware.cache.FetchFormCacheMiddleware
clickjacking: 指攻击者在自己的病毒网站上添加诱惑性的按钮,使用iframe技术将受攻击网站(比如银行)加载到自己的网站上去,并将其设置为透明;诱惑用户点击执行受攻击的网站操作。
中间件放置顺序
- Securitywiddleware:应该放到最前面。因为这个中间件并不需要依赖任何其他的中间件。如果你的网站同时支持http协议和https协议,并且你想让用户在使用http协议的时候重定向到https协议,那么就没有必要让他执行下面一大串中间件再重定向,这样效率更高。
- UpdateCacheMiddleware:应该在SessionMiddleware,GZipMiddleware,LocaleMiddleware之前。
- GZipMiddleware。
- ConditionalGetMiddleware。
- SessionMiddleware。
- LocaleMiddleware。
- CommonMiddleware。
- CsrfViewliddleware。
- AuthenticationMiddleware。
- MessageMiddleware。
- FetchFromCacheMiddleware。
- FlatpageFallbackMiddleware。
- 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()注册接收者函数,接收一个信号;当信号发生时,会调用接收者函数。
Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)parameters:receiver - 回调函数连接此信号sender - 指定发送者weak - Django在默认情况下将信号处理程序存储为弱引用。因此,如果您的接收器是一个本地函数,那么它可能是垃圾收集的。为了防止这种情况发生,当您调用signal.connect()方法时,传递 weak=False。dispatch_uid - 在可能发送重复信号的情况下,信号接收器的唯一标识符。有关更多信息,请参见防止重复信号 https://docs.djangoproject.com/zh-hans/2.0/topics/signals/#preventing-duplicate-signals。
监听信号方法:
定义接收器
连接接收器
连接到特定发送者发送的信号
防止重复信号
定义和发送信号
定义信号
发送信号
断开信号
