1. 中間件執行流程
Django 1.10之前,請求到達第一個中間件,會找到最後一箇中間件的 process_response 返回:
而Django 1.10之後,會找到自己的 process_response 返回:
中間件是有序執行的!
2. 中間件原理淺析
中間件是類,首先查看Django源碼中的中間件:
所以,我們寫的中間件也要繼承MiddlewareMixin類,這裏把中間件類寫在middleware.py,並放置在與manage.py同級目錄下:
middleware.py:
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self, request):
print('m1.process_request')
def process_response(self, request, response):
print('m1.process_response')
return response
class M2(MiddlewareMixin):
def process_request(self, request):
print('m2.process_request')
def process_response(self, request, response):
print('m2.process_response')
return response
"""
m1.process_request
m2.process_request
test
m2.process_response
m1.process_response
"""
還要在配置文件 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',
'middleware.M1',
'middleware.M2'
]
可以看到 先執行process_request再執行process_response
。還有個process_view方法:
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self, request):
print('m1.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m1.process_view')
# return callback(*callback_args, **callback_kwargs)
def process_response(self, request, response):
print('m1.process_response')
return response
class M2(MiddlewareMixin):
def process_request(self, request):
print('m2.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m2.process_view')
# return callback(*callback_args, **callback_kwargs)
def process_response(self, request, response):
print('m2.process_response')
return response
"""
m1.process_request
m2.process_request
m1.process_view
m2.process_view
test
m2.process_response
m1.process_response
"""
process_request有返回值會直接執行自己的process_response。如果執行的是process_view有返回值,會跳過下一個中間件的process_view,執行視圖函數(這裏是自動調用的
)。接下來,不是執行自己的process_response返回給用戶,而是 最開始
的process_response(把所有的process_response都執行一遍
):
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self, request):
print('m1.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
# print(callback, callback_args, callback_kwargs) #<function test at 0x7fd39b986040> () {}
print('m1.process_view')
response = callback(request, *callback_args, **callback_kwargs)
return response
def process_response(self, request, response):
print('m1.process_response')
return response
class M2(MiddlewareMixin):
def process_request(self, request):
print('m2.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m2.process_view')
def process_response(self, request, response):
print('m2.process_response')
return response
"""
m1.process_request
m2.process_request
m1.process_view
test
m2.process_response
m1.process_response
"""
中間件的類中還有 process_exception 方法,這是關於異常的方法。爲了進行測試首先在視圖函數中使代碼產生異常:
def test(request):
print('test')
a = int('erics')
return HttpResponse()
通過下面的代碼測試發現,請求經過所有的process_request、process_view到達視圖函數,沒有錯誤的情況下會執行process_response,出現錯誤會執行process_exception在執行process_response:
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self, request):
print('m1.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
# print(callback, callback_args, callback_kwargs) #<function test at 0x7fd39b986040> () {}
print('m1.process_view')
# response = callback(request, *callback_args, **callback_kwargs)
# return response
#
def process_response(self, request, response):
print('m1.process_response')
return response
def process_exception(self, request, exception):
print('m1.process_exception')
class M2(MiddlewareMixin):
def process_request(self, request):
print('m2.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m2.process_view')
def process_response(self, request, response):
print('m2.process_response')
return response
def process_exception(self, request, exception):
print('m2.process_exception')
"""
m1.process_request
m2.process_request
m1.process_view
m2.process_view
test
m2.process_exception
m1.process_exception
m2.process_response
m1.process_response
"""
在process_exception使用HttpResponse函數返回異常,可以看這個時候中間件方法的執行順序:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class M1(MiddlewareMixin):
def process_request(self, request):
print('m1.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
# print(callback, callback_args, callback_kwargs) #<function test at 0x7fd39b986040> () {}
print('m1.process_view')
# response = callback(request, *callback_args, **callback_kwargs)
# return response
#
def process_response(self, request, response):
print('m1.process_response')
return response
def process_exception(self, request, exception):
print('m1.process_exception')
class M2(MiddlewareMixin):
def process_request(self, request):
print('m2.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m2.process_view')
def process_response(self, request, response):
print('m2.process_response')
return response
def process_exception(self, request, exception):
# print('m2.process_exception')
return HttpResponse('出現異常!')
"""
m1.process_request
m2.process_request
m1.process_view
m2.process_view
test
m2.process_response
m1.process_response
"""
異常被M2的process_exception處理了,跳過M1的process_exception,直接執行process_response。頁面也不沒有報錯了:
中間件中還有process_template_response方法,但是這個方法在視圖函數的返回值中有render方法纔會執行,沒有render方法不會被執行,所以這裏修改視圖函數:
class Foo:
def __init__(self, req):
self.req = req
def render(self):
return HttpResponse()
def test(request):
obj = Foo(request)
return obj
接下來可以看到process_template_response方法被執行,
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class M1(MiddlewareMixin):
def process_request(self, request):
print('m1.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m1.process_view')
def process_response(self, request, response):
print('m1.process_response')
return response
def process_exception(self, request, exception):
print('m1.process_exception')
def process_template_response(self, request, response):
"""
視圖函數的返回值中有render方法纔會執行
:param request:
:param response:
:return:
"""
print('m1.process_template_response')
return response
class M2(MiddlewareMixin):
def process_request(self, request):
print('m2.process_request')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('m2.process_view')
def process_response(self, request, response):
print('m2.process_response')
return response
def process_exception(self, request, exception):
return HttpResponse('出現異常!')
def process_template_response(self, request, response):
print('m2.process_template_response')
return response
"""
m1.process_request
m2.process_request
m1.process_view
m2.process_view
m2.process_template_response
m1.process_template_response
m2.process_response
m1.process_response
"""
process_template_response方法的使用,可以幫助我們把一些可以複用的功能組件拆分出來,如JSON序列化:
class Foo:
def __init__(self, req, status, msg):
self.req = req
self.status = status
self.msg = msg
def render(self):
import json
ret = {
'status': self.status,
'msg': self.msg
}
return HttpResponse(json.dumps(ret))
def test(request):
return Foo(request, True, '錯誤信息!')
在視圖函數中打造功能,改造另外一個對象,讓這個對象把我們的數據封裝起來:
class JsonResponse:
"""
內部幫助我們序列化,
"""
def __init__(self, req, status, msg):
self.req = req
self.status = status
self.msg = msg
def render(self):
import json
ret = {
'status': self.status,
'msg': self.msg
}
return HttpResponse(json.dumps(ret))
def test(request):
return JsonResponse(request, True, '錯誤信息!')
視圖函數返回的對象且對象中有render方法纔會執行process_template_response方法。
3. 中間件的應用
中間件可以對 所有請求或一部分請求做批量處理
。使用緩存時,需要對 所有請求進行判斷
,緩存中如果有就把緩存中的數據返回,沒有就執行視圖函數。對所有的請求進行判斷就需要使用到中間件。