[Django高階]理解django中的中介軟體機制和執行順序 [Django高階]理解django中的中介軟體機制和執行順序
[Django高階]理解django中的中介軟體機制和執行順序
原文來自 Understanding Django Middlewares, 這篇文章從整體上介紹了django中中介軟體定義,作用,和怎麼樣自己寫中介軟體 –orangleliu。
注:middleware 和中介軟體在下面文章中含義相同,不完全翻譯了
假設你已經閱讀了 Django官方文件middleware部分 . 下面會盡可能詳盡的介紹文件中提到的知識,但是還是希望你熟悉 middleware
這篇文章中我們將討論下面內容:
- 什麼是 middleware
- 什麼時候使用 middleware
- 我們寫 middleware 必須要記住的東西
- 寫一些 middlewares 來理解中介軟體的工作過程和要點
什麼是 middleware
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.
- 1
什麼時候使用 middleware
如果你想修改請求,例如被傳送到view中的HttpRequest物件。 或者你想修改view返回的HttpResponse物件,這些都可以通過中介軟體來實現。
可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。
Django 提供了一些預設的 middleware,例如:
AuthenticationMiddleware
大家可能頻繁在view使用request.user
AuthenticationMiddleware
。
Django 這樣修改request物件的:
https://github.com/django/django/blob/master/django/contrib/auth/middleware.py#L22
- 1
例如你有一個應用,它的使用者是不同時區的人們。你想讓他們在訪問任何頁面的時候都能顯示正確的時區,想讓所有的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
- 1
- 2
- 3
- 4
- 5
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"
- 1
- 2
- 3
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',
)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
對任意的一個url傳送請求, 下面的資訊將會列印在runserver的控制檯。
Middleware executed
- 1
修改 BookMiddleware.process_request
如下
class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
- 1
- 2
- 3
- 4
再次訪問一個url,將會引起一個錯誤。
'WSGIRequest' object has no attribute 'user'
- 1
這是因為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',
)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
訪問一個url,runserver控制檯列印如下
Middleware executed
<username>
- 1
- 2
這說明middlewares處理request的順序跟 settings.MIDDLEWARE_CLASSES 中列出的順序是一致的。
你可以進一步證實,middleware.py新增另外一個middleware
class AnotherMiddleware(object):
def process_request(self, request):
print "Another middleware executed"
- 1
- 2
- 3
把它也加到 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',
)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
現在的輸出是:
Middleware executed
<username>
Another middleware executed
- 1
- 2
- 3
在process_request方法中返回HttpResponse,把BookMiddleware改成下面這樣:
class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
return HttpResponse("some response")
- 1
- 2
- 3
- 4
- 5
嘗試下任何一個url,會得到如下輸出:
Middleware executed
<username>
- 1
- 2
你會注意到下面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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
訪問一些url,得到如下的輸出
Middleware executed
<username>
Another middleware executed
AnotherMiddleware process_response executed
BookMiddleware process_response executed
- 1
- 2
- 3
- 4
- 5
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()
將會被省略,不會被執行。
<script>
(function(){
function setArticleH(btnReadmore,posi){
var winH = $(window).height();
var articleBox = $("div.article_content");
var artH = articleBox.height();
if(artH > winH*posi){
articleBox.css({
'height':winH*posi+'px',
'overflow':'hidden'
})
btnReadmore.click(function(){
if(typeof window.localStorage === "object" && typeof window.csdn.anonymousUserLimit === "object"){
if(!window.csdn.anonymousUserLimit.judgment()){
window.csdn.anonymousUserLimit.Jumplogin();
return false;
}else if(!currentUserName){
window.csdn.anonymousUserLimit.updata();
}
}
articleBox.removeAttr("style");
$(this).parent().remove();
})
}else{
btnReadmore.parent().remove();
}
}
var btnReadmore = $("#btn-readmore");
if(btnReadmore.length>0){
if(currentUserName){
setArticleH(btnReadmore,3);
}else{
setArticleH(btnReadmore,1.2);
}
}
})()
</script>
</article>