這個以前本地寫的那個django測試項目提及,那時候寫了個練手的項目,目的是爲了熟悉總結django2.0和django1.8的區別。不試不知道,一試就發現了許許多多的坑以及bug,把這些坑以及bug解決完了以後,打算寫篇文章記錄下我遇到的問題以及解決方法和思路。html
原由是當我在自強學堂的django課堂上,看到了有一個demo,這個demo具體實現的效果就是當網站在正式環境上運行的時候,爲了安全起見,將DEBUG改成False(關閉調試模式),可是致使網站發生錯誤沒法查看錯誤詳情。
因此demo主要的就是寫一個經過中間件識別身份的方式,若是是管理員則能夠看到網站錯誤詳情,若是是普通訪問者或者遊客則返回的是簡單的錯誤碼。前端
中間件識別登陸身份,判斷是否爲管理員,若是是管理員的話,當網站出現錯誤的時候則會顯示錯誤詳情;若是是普通遊客的話則單純顯示錯誤碼,不顯示詳情。django
我整理了一下有關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.安全
- 簡而言之,Middleware就是可以修改Django中response/request對象的鉤子,咱們能夠利用Middleware來實如今請求到達view視圖函數前的一些操做。
- 舉個最簡單的例子:一個管理後臺判斷用戶是否登陸,就是判斷request對象中的用戶,若是對象中的用戶是不存在的,則重定向到登陸頁面。
大概瞭解了一下中間件是什麼東西,能夠用來作什麼,咱們大體分析一下中間件的處理流程。session
相信上圖在不少django教程中看到過,上圖都是Django中內置的一些中間件,這些中間件都放在Django中settings.py文件中的MIDDLEWARE_CLASSES。(django 2.0版本後放在MIDDLEWARES上)
而後在http請求階段,在view調用以前了,django會將MIDDLEWARE_CLASSES中的中間件都執行一遍。而這裏面的主要的幾個鉤子函數:app
- process_request()、process_view()會從上到下挨個執行一遍;
- process_exception()、process_template_response()、process_response()則會從下到上挨個執行一遍。
具體這幾個函數做用,以及django內置中間件分別負責什麼做用單獨會另外寫篇文章總結。
這裏的主要使用到process_exception()鉤子,這個鉤子函數只有當view拋出異常的時候會觸發,因此很適合返回網站的錯誤詳情。
終於到實操環節了,思路上面提到過了,這裏具體代碼實現的邏輯:
用戶經過登陸界面登陸到平臺,經過內置的auth模塊保存用戶登陸到會話中。
若是網站出現錯誤信息時,這時異常拋到本身的中間件時,捕獲views視圖函數拋出的異常,判斷request中的user對象是否爲超級管理員,若是是的話,則返回一個錯誤詳細響應到前端,不是的話正常返回500錯誤碼。框架
如下是views.py中關於用戶登陸模塊,具體登陸請求會提交到這裏:ide
def login(request): if request.method == 'POST': user_name = request.POST['user_name'] user_password = request.POST['user_password'] user = auth.authenticate(username=user_name, password=user_password) if user is not None: auth.login(request, user) return redirect('/index') else: return render(request, 'login.html', {'login_error': '登陸失敗,密碼>錯誤'}) return render(request, 'login.html')
from django.views.debug import technical_500_response import sys class UserBasedExceptionMiddleware(object): def process_exception(self, request, exception): if request.user.is_superuser: return technical_500_response(request, *sys.exc_info())
MIDDLEWARE_CLASSES = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'app.middleware.UserBasedExceptionMiddleware', ]
* 這樣就能實現返回網站錯誤信息啦:函數
作到這裏想必是作完了,可是我更想談談我在這過程遇到兩個問題,在這兩個問題裏可能花費的時間更多。性能
因爲將DEBUG模式設置False以後,從新啓動項目後,發現全部的靜態文件都沒法訪問。
查了一下官方文檔,官方文檔給出的解釋就是,在開發過程當中,django會提供django.contrib.staticfiles幫助管理靜態文件,而開啓這一功能,除了須要包含在INSTALL_APPS以外,還須要將DUBUG模式改成True。
因此略微分析,大概就知道咱們靜態文件404訪問不到的緣由了,簡單來講就是django提供給咱們的靜態文件路由功能不能用了,致使404錯誤。固然官方貼心的給出了額外的建議:
- 使用serve()視圖提供靜態資源訪問服務。
- 經過Nginx、Apache等代理靜態資源。
而對於在生產環境下,官方更推薦的是第二種方法,緣由無非就兩個:
- django提供的serve()視圖僅用於開發輔助使用,不適合生產使用。
- Nginx處理靜態資源有着更強的性能優點。
這裏咱們就先經過上面提到第一種方法來解決「靜態資源404的問題」。
而關於Django和Nginx部署Django項目的,以前在一篇文章裏介紹過,你們有興趣能夠去看看:http://www.javashuo.com/article/p-alzesnxu-cz.html
from django.contrib import admin from django.views.static import serve from django.urls import path,re_path urlpatterns = [ path('admin/', admin.site.urls), ] if not settings.DEBUG: urlpatterns += [ re_path(r'^static/(?P<path>.*)$', serve, {'document_root': 'static', }), ]
* 重啓django項目,大功告成,第一個問題解決
我把這個中間件文件複製到另一個項目中,打算一勞永逸,沒想到居然出錯了:
首先我檢查的就是版本的問題,我看了一下剛剛中間件成功的那個項目是屬於django1.8.2版本,而現在報錯的項目沒想到是django2.0版本。
我查了官方文檔,如下是官方文檔的解釋,有興趣的能夠了解如下:
https://docs.djangoproject.com/zh-hans/2.2/topics/http/middleware/
而參考官網給出的解決方法,就是經過Django提供的django.utils.deprecation.MiddlewareMixin類,它可以輕鬆兼容新版的MIDDLEWARE和舊版的MIDDLEWARE_CLASSES。
from django.views.debug import technical_500_response import sys try: from django.utils.deprecation import MiddlewareMixin except: MiddlewareMixin = object class UserBasedExceptionMiddleware(MiddlewareMixin): def process_exception(self, request, exception): if request.user.is_superuser: return technical_500_response(request, *sys.exc_info())
重啓項目,沒有報錯:
沒想到一個小小的中間件功能實現居然途中遇到了這麼多問題,可是在遇到問題的過程當中排查問題的方向都是大體正確的,提及不足的地方仍是對於Django的總體框架知識不夠紮實。
因爲Django上不少東西都是等到須要用的時候,纔會去查找資料,這也致使不少問題不能第一時間反應解決。因此對於Django新版本和舊版本之間的區別沒有及時的整理,以及相似中間件重要的知識點沒有及時概括。 之後儘可能抽時間整理一下Django的框架知識,固然工做學習過程當中踩到的坑可以分享,也是對本身另外的一種學習提高。