django 1.11
中间件的制造工厂是一个方法接收get_response 作为入参返回一个中间件。一个中间件是一个可调用的对象,接收一个request返回一个response,就像view.
middleware也可以写作一个方法
def simple_middleware(get_response):
def middleware(request):
response=get_response(request)
return response
return middleware
或者写作一个类class
class SimpleMiddleware(object):
def __init__(self,get_response):
self.get_response=get_response
def __call__(self,request):
response=self.get_response(request)
return response
get_response由django提供,它可能是一个view(如果这个middleware排在最后)也可能是链条中的下一个middleware。当前的中间件不需要知道它具体是什么,它仅仅代表了生成的结果
django只需要get_response一个参数来初始化中间件,所以你定义__init__()时不能添加其他的参数。
__call__()方法每次请求都会被调用一次,__init__()方法仅在web服务器开启的时候被调用一次。
当middleware不应该被启用时,middleware的__init__方法会抛出MiddlewareNotUsed,然后django会将此middleware从中间价进程中移除,并且会产生一条debug级别的日志。
在MIDDLEWARE列表中的的中间件的顺序会影响他们依赖的中间件。比如AuthenticationMiddleware在session中存储了已认证的用户,所以它必须在SessionMiddleware之后运行
在request阶段,请求先从MIDDLEWARE列表中由上到下穿过,然后到达view,然后response将由middleware中由下到上穿过返回.
如果有一个middleware在request阶段没有调用get_response,直接返回response,那么剩下的middleware(包括view)也就不会见到request或response了。这个response会倒序穿过request穿过的middleware.
class形式的middleware通常包括一下处理方法:
process_request(request)
process_reponse(request,response)
process_view(request,view_func,view_args,view_kwargs) 在调用view之前被调用
view_func :一个真正的方法对象,而不是方法名字的字符串
view_args和view_kwargs分别是view_func的位置入参和关键字入参,他们都不需要传入request
它返回 None和HttpResponse中的一个,如果是none则继续调用下一个middleware的process_view,如果是HttpResponse则不会再调用view而是调用response middleeware来处理response后直接返回结果
如果通过middleware在调用view之前访问了request.POST,将会阻止任何view在运行过程中可能修改request的上传器的行为
CscrViewMiddleware可以看作一个异常处理器,它提供csrf_exempt()和csrf_protect()装饰器可以让view明确控制CSRF应该确认的点
process_exception(request,exception) 当view抛出异常时 被调用
它返回None和HttpResponse中的一个,如果返回Httpresponse,则reponse middleware继续被应用于该response,否则由默认的异常处理来处理它。
response经过中间件的顺序是由下向上,process_exception被调用的顺序也是由下向上。如果一个exception middleware返回一个response,那么该中间件之上的response中间件就不会被调用了。
process_template_response(request,response)在view刚被调用完后被调用,执行顺序同response
request: HttpRequest对象。
response:由view或middleware返回的TemplateResponse对象。如果一个response实例有render()方法,就表明它是一个TemplateResponse对象。
它必须返回一个执行了render方法的response。 在该方法中可以改变 传入的response的response.template_name 和response.context_data 或者创建一个新的Tempalteresponse返回
response不需要明确的被渲染,一旦template response middleware被调用,response会自动被选染。
TemplateResponse保留了view计算出reponse的上下文,最后输出response是直到它被需要时才在reponse process中计算完成的
处理 流式reponse
与HttpResponse不同,StreamingHttpResponse没有属性content.当middleware需要使用content时,必须判断response是否为streaming response 来响应的调整他们的行为。
if reponse.streaming:
reponse.streaming_content=wrap_streaming_content(response.streaming_content)
else:
reponse.content=alter_content(reponse.content)
应该将streaming reponse 预估为较大的,内存无法承担的对象,应使用generator来逐步使用,如下:
def wrap_streaming_content(content):
for chunk in content:
yield alter_content(chunk)
处理异常
django会自动将view或middleware产生的异常转化成对应的 http错误 响应码页面
明确的异常映射成4xx错误码,未知异常映射成500错误码
这种映射发生在每个middleware之前和之后,这个每个middleware都可以确定通过get_reponse可以收到http reponse,而不用使用try/except来处理get_reponse的返回值,打比方说后面的middleware中抛出一个Http404的异常,当前的middleware不会接收到这个异常,而是获取到一个Httpresponse对象,及一个404的status_code。
缓存中间件
如果开启了缓存中间键,它会作用于url映射的每个页面
规范的设置方式是 将UpdateCacheMiddleware作为第一个中间键,FetchFromCacheMiddleware作为最后一个,以便request在最后一步经过FetchFromCacheMiddleware来通过缓存返回response ,response在最后一步经过UpdateCacheMiddleware来更新一个可缓存的response的缓存:
MIDDLEWARE=[
'django.middleware.cache.UpdateCacheMiddleware',
...
'django.middleware.cache.FetchFromCacheMiddleware'
]
只有请求方式为get或head且状态码为200的内容会被缓存
页面缓存的保存秒数 由response响应头"Cache-Control"的"max-age"属性值决定,如果没有找到该属性则根据settings.py中的CACHE_MIDDLEWARE_SECONDS的值来确定
这个中间件期望请求的响应头与get类型请求的响应头几乎一致
当发生攻击时,process_request将会浅复制一个原始的response对象,返回。
页面将会根据请求头在response's "vary" header中列出的内容来缓存
这个中间键也可以设置reponse响应头的ETag,Last-Modified,Expires和Cache-Control值
使用时,在view中from django.views.decorators.cache import cache_page ,然后在需要缓存的函数页面上加上 @cache_page(seconds)即可,seconds表示缓存的有效秒数。
在settings.py中的CACHES设置 缓存的数据应该保存在哪里,数据库,文件系统或直接保存到内存。
可用的值有:
Memcached
django默认支持的快速,高效的缓存 key-value数据库,完全基于内存的缓存服务,它只是提供了一个快速的删除,检索,添加 缓存里数据的接口。
安装完Memcached 后需要安装相关依赖 ,常用的有python-memcached和pylibmc .
在django中起用Memcached 作为缓存,将backend设置为django.core.cache.backends.memcached.MemcachedCache或者
django.core.cache.backends.memcached.PlLibMCCache
location设置为‘ip:port',或者 unix:path ,path为Memcached Unix socket文件的路径(例 'unix:/tmp/memcached.sock') ,例
CACHES={
'default':{
'BACKEND':'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION':'127.0.0.1:11211',
}
}
当backends使用pylibmc时,LOCATION应设置为’/tmp/memcached.sock'
Memcached有一个比较好的特性是可以在多个服务器上共享缓存。这意味着可以在不同的服务器上各自运行一个Memcache,不需要复制缓存值,应用工程可以把这个集群看作单个的缓存服务器,启用这种特性需要将所有的地址添加进LOCATION,可以以list或分号,逗号分隔的字符串表示。如:
CACHES={
‘default':{
'BACKEDN':'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION':[
'172.19.26.240:11211',
'172.19.26.142:11212',
'172.19.26.244:11213',
]
}
}
Memcached是一个基于缓存的数据库,所以如果服务器宕机了,存储于其中的数据就会丢失。
中间件的应用场景,一般用来做IP限制(放在中间件的列表中,阻止某些IP访问;),URL过滤(用户信息验证),缓存。