這篇文章中我們将讨論下面内容:
- 什麼是 middleware
- 什麼時候使用 middleware
- 我們寫 middleware 必須要記住的東西
- 寫一些 middlewares 來了解中間件的工作過程和要點
Middlewares 是修改 Django request 或者 response 對象的鈎子. 下面是Django 文檔中的一段描述。
Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output.
如果你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view傳回的HttpResponse對象,這些都可以通過中間件來實作。
可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實作。
Django 提供了一些預設的 middleware,例如:
AuthenticationMiddleware
大家可能頻繁在view使用
request.user
吧。 Django想在每個view執行之前把user設定為request的屬性,于是就用了一個中間件來實作這個目标。是以Django提供了可以修改request 對象的中間件
AuthenticationMiddleware
。
Django 這樣修改request對象的:
https://github.com/django/django/blob/master/django/contrib/auth/middleware.py#L22
例如你有一個應用,它的使用者是不同時區的人們。你想讓他們在通路任何頁面的時候都能顯示正确的時區,想讓所有的views中都能得到使用者自己的timezone資訊。 這種情況下可以用session來解決,是以你可以像下面添加一個 middleware:
class TimezoneMiddleware(object):
def process_request(self, request):
# Assuming user has a OneToOneField to a model called Profile
# And Profile stores the timezone of the User.
request.session['timezone'] = request.user.profile.timezone
TimezoneMiddleware 是依賴于 request.user的,request.user 是通過
AuthenticationMiddleware
來設定的。 是以在
settings.MIDDLEWARE_CLASSES
配置中,TimezoneMiddleware 一定要在 AuthenticationMiddleware 之後。
下面的例子可以得到關于中間件順序的更多體會。
使用middleware時應該記住的東西
- middlewares 的順序非常重要
- 一個middleware隻需要繼承 object 類
- 一個middleware可以實作一些方法并且不需要實作所有的方法
- 一個middleware可以實作 process_request(方法) 但是不可以實作 process_response(方法)和 process_view 方法。 這些都很常見,Django提供了很多middlewares可以做到。
- 一個middleware可以實作 process_response 方法,但是不需要實作 process_request 方法
AuthenticationMiddleware 隻實作了對請求的處理,并沒有處理響應. 參照文檔
GZipMiddleware 隻實作了對響應的處理,并沒有實作對請求和view的處理 參見文檔
寫一些 middlewares
首先确認下你有一個Django項目,需要一個url和一個view,并且可以進入這個view。下面我們會對request.user做幾個測試,确認權限設定好了,并可以在view中正确列印 request.user 的資訊。
在任意一個app中建立middleware.py檔案。
我有一個叫做books的app,是以檔案的位置是
books/middleware.py
class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
MIDDLEWARE_CLASSES 中添加這個中間件
MIDDLEWARE_CLASSES = (
'books.middleware.BookMiddleware',
'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',
)
對任意的一個url發送請求, 下面的資訊将會列印在runserver的控制台。
Middleware executed
修改
BookMiddleware.process_request
如下
class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
再次通路一個url,将會引起一個錯誤。
'WSGIRequest' object has no attribute 'user'
這是因為request對象還沒有設定user屬性呢。
現在我們改變下 middlewares的順序,
BookMiddleware
放在
AuthenticationMiddleware
之後。
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'books.middleware.BookMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
通路一個url,runserver控制台列印如下
Middleware executed
<username>
這說明middlewares處理request的順序跟 settings.MIDDLEWARE_CLASSES 中列出的順序是一緻的。
你可以進一步證明,middleware.py添加另外一個middleware
class AnotherMiddleware(object):
def process_request(self, request):
print "Another middleware executed"
把它也加到
MIDDLEWARE_CLASSES
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'books.middleware.BookMiddleware',
'books.middleware.AnotherMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
現在的輸出是:
Middleware executed
<username>
Another middleware executed
在process_request方法中傳回HttpResponse,把BookMiddleware改成下面這樣:
class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
return HttpResponse("some response")
嘗試下任何一個url,會得到如下輸出:
Middleware executed
<username>
你會注意到下面2個事情:
- 不管你通路哪個url,自己寫的view 處理方法都不執行了,隻有 “some response”這樣一種響應。
- AnotherMiddleware.process_request 不在被執行
是以如果 Middleware的process_request方法中傳回了HttpResponse對象,那麼它之後的中間件将被略過, view中的處理方法也被略過。 是以在實際的項目中很少會這麼幹(不過也有些項目會,例如做代理)
注釋掉
"return HttpResponse("some response")"
,兩個 middleware 才能正常的處理請求。
使用 process_response
給這兩個middleware添加 process_response方法
class AnotherMiddleware(object):
def process_request(self, request):
print "Another middleware executed"
def process_response(self, request, response):
print "AnotherMiddleware process_response executed"
return response
class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
return HttpResponse("some response")
#self._start = time.time()
def process_response(self, request, response):
print "BookMiddleware process_response executed"
return response
通路一些url,得到如下的輸出
Middleware executed
<username>
Another middleware executed
AnotherMiddleware process_response executed
BookMiddleware process_response executed
AnotherMiddleware.process_response()
在
BookMiddleware.process_response()
之前執行 而
AnotherMiddleware.process_request()
BookMiddleware.process_request()
之後執行. 是以
process_response()
執行的順序跟 process_request正好相反.
process_response()
執行的順序是從最後一個中間件執行,到倒數第二個,然後直到第一個中間件.
process_view
Django 按順序執行中間件
process_view()
的方法,從上到下。 類似process_request()方法執行的順序。
是以如果任何一個
process_view()
傳回了HttpResponse對象,那麼在它後面
process_view()
将會被省略,不會被執行。
小蟒蛇