django 從請(qǐng)求到返回都經(jīng)歷了什么
ruserver是使用django自己的web server,主要用于開(kāi)發(fā)和調(diào)試中驶社, 部署到線上環(huán)境一般使用nginx+uwsgi模式
看一下manager.py的源碼企量,你會(huì)發(fā)現(xiàn)上面的命令其實(shí)是通過(guò)Django的execute_from_command_line方法執(zhí)行了內(nèi)部實(shí)現(xiàn)的runserver命令,那么現(xiàn)在看一下runserver具體做了什么亡电。届巩。
通過(guò)源碼分析可知, ruserserver主要完成兩件事:
1). 解析參數(shù)份乒,并通過(guò)django.core.servers.basehttp.get_internal_wsgi_application方法獲取wsgi handler;
2). 根據(jù)ip_address和port生成一個(gè)WSGIServer對(duì)象恕汇,接受用戶請(qǐng)求
get_internal_wsgi_application的源碼如下:
通過(guò)上面的代碼我們可以知道,Django會(huì)先根據(jù)settings中的WSGI_APPLICATION來(lái)獲取handler或辖;
在創(chuàng)建project的時(shí)候瘾英,Django會(huì)默認(rèn)創(chuàng)建一個(gè)wsgi.py文件,而settings中的WSGI_APPLICATION配置也會(huì)默認(rèn)指向這個(gè)文件孝凌》脚兀看一下這個(gè)wsgi.py文件,其實(shí)它也和上面的邏輯一樣蟀架,最終調(diào)用get_wsgi_application實(shí)現(xiàn)瓣赂。
Django和其他Web框架一樣榆骚,HTTP的處理流程基本類似:接受request,返回response內(nèi)容煌集。Django的具體處理流程大致如下:
在通過(guò)django-admin.py創(chuàng)建project的時(shí)候妓肢,Django會(huì)自動(dòng)生成默認(rèn)的settings文件和manager.py等文件,在創(chuàng)建WSGIServer之前會(huì)執(zhí)行下面的引用:
from django.conf import settings
上面引用在執(zhí)行時(shí)苫纤,會(huì)讀取os.environ中的DJANGO_SETTINGS_MODULE配置碉钠,加載項(xiàng)目配置文件,生成settings對(duì)象卷拘。所以喊废,在manager.py文件中你可以看到,在獲取WSGIServer之前栗弟,會(huì)先將project的settings路徑加到os路徑中污筷。
不管是使用runserver還是uWSGI運(yùn)行Django項(xiàng)目,在啟動(dòng)時(shí)都會(huì)調(diào)用django.core.servers.basehttp中的run()方法
創(chuàng)建一個(gè)django.core.servers.basehttp.WSGIServer類的實(shí)例乍赫,之后調(diào)用其serve_forever()方法啟動(dòng)HTTP服務(wù)瓣蛀。run方法的源碼如下:
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
? ? server_address = (addr, port)
? ? if threading:
? ? ? ? httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
? ? else:
? ? ? ? httpd_cls = WSGIServer
? ? httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
? ? if threading:
? ? ? ? # ThreadingMixIn.daemon_threads indicates how threads will behave on an
? ? ? ? # abrupt shutdown; like quitting the server by the user or restarting
? ? ? ? # by the auto-reloader. True means the server will not wait for thread
? ? ? ? # termination before it quits. This will make auto-reloader faster
? ? ? ? # and will prevent the need to kill the server manually if a thread
? ? ? ? # isn't terminating correctly.
? ? ? ? httpd.daemon_threads = True
? ? httpd.set_app(wsgi_handler)
? ? httpd.serve_forever()
如上,我們可以看到:在創(chuàng)建WSGIServer實(shí)例的時(shí)候會(huì)指定HTTP請(qǐng)求的Handler雷厂,
上述代碼使用WSGIRequestHandler惋增。當(dāng)用戶的HTTP請(qǐng)求到達(dá)服務(wù)器時(shí),
WSGIServer會(huì)創(chuàng)建WSGIRequestHandler實(shí)例改鲫,使用其handler方法來(lái)處理HTTP請(qǐng)求(其實(shí)最終是調(diào)用wsgiref.handlers.BaseHandler中的run方法處理)诈皿。
WSGIServer通過(guò)set_app方法設(shè)置一個(gè)可調(diào)用(callable)的對(duì)象作為application,上面提到的handler方法最終會(huì)調(diào)用設(shè)置的application處理request钩杰,并返回response纫塌。
其中,WSGIServer繼承自wsgiref.simple_server.WSGIServer讲弄,而WSGIRequestHandler繼承自wsgiref.simple_server.WSGIRequestHandler措左,wsgiref是Python標(biāo)準(zhǔn)庫(kù)給出的WSGI的參考實(shí)現(xiàn)。其源碼可自行到wsgiref參看避除,這里不再細(xì)說(shuō)怎披。
第二步中說(shuō)到的application,在Django中一般是django.core.handlers.wsgi.WSGIHandler對(duì)象瓶摆,WSGIHandler繼承自django.core.handlers.base.BaseHandler凉逛,這個(gè)是Django處理request的核心邏輯,它會(huì)創(chuàng)建一個(gè)WSGIRequest實(shí)例群井,而WSGIRequest是從http.HttpRequest繼承而來(lái)
上面提到的BaseHandler中有個(gè)get_response方法状飞,該方法會(huì)先加載Django項(xiàng)目的ROOT_URLCONF,然后根據(jù)url規(guī)則找到對(duì)應(yīng)的view方法(類),view邏輯會(huì)根據(jù)request實(shí)例生成并返回具體的response诬辈。
在Django返回結(jié)果之后酵使,第二步中提到wsgiref.handlers.BaseHandler.run方法會(huì)調(diào)用finish_response結(jié)束請(qǐng)求,并將內(nèi)容返回給用戶
首先給大家分享兩個(gè)網(wǎng)上看到的Django流程圖:
流程圖一
流程圖二
上面的兩張流程圖可以大致描述Django處理request的流程焙糟,按照流程圖2的標(biāo)注口渔,可以分為以下幾個(gè)步驟:
1. 用戶通過(guò)瀏覽器請(qǐng)求一個(gè)頁(yè)面
2.請(qǐng)求到達(dá)Request Middlewares,中間件對(duì)request做一些預(yù)處理或者直接response請(qǐng)求
3.URLConf通過(guò)urls.py文件和請(qǐng)求的URL找到相應(yīng)的View
4.View Middlewares被訪問(wèn)穿撮,它同樣可以對(duì)request做一些處理或者直接返回response
5.調(diào)用View中的函數(shù)
6.View中的方法可以選擇性的通過(guò)Models訪問(wèn)底層的數(shù)據(jù)
7.所有的Model-to-DB的交互都是通過(guò)manager完成的
8.如果需要缺脉,Views可以使用一個(gè)特殊的Context
9.Context被傳給Template用來(lái)生成頁(yè)面
? ? a.Template使用Filters和Tags去渲染輸出
? ? b.輸出被返回到View
? ? c.HTTPResponse被發(fā)送到Response Middlewares
? ? d.任何Response Middlewares都可以豐富response或者返回一個(gè)完全不同的response
? ? e.Response返回到瀏覽器,呈現(xiàn)給用戶
上述流程中最主要的幾個(gè)部分分別是:Middleware(中間件悦穿,包括request, view, exception, response)攻礼,URLConf(url映射關(guān)系),Template(模板系統(tǒng))咧党,下面一一介紹一下秘蛔。
Middleware并不是Django所獨(dú)有的東西,在其他的Web框架中也有這種概念傍衡。在Django中,Middleware可以滲入處理流程的四個(gè)階段:request负蠕,view蛙埂,response和exception,相應(yīng)的遮糖,在每個(gè)Middleware類中都有rocess_request绣的,process_view, process_response 和 process_exception這四個(gè)方法欲账。你可以定義其中任意一個(gè)活多個(gè)方法屡江,這取決于你希望該Middleware作用于哪個(gè)處理階段。每個(gè)方法都可以直接返回response對(duì)象赛不。
Middleware是在Django BaseHandler的load_middleware方法執(zhí)行時(shí)加載的惩嘉,加載之后會(huì)建立四個(gè)列表作為處理器的實(shí)例變量:
_request_middleware:process_request方法的列表
_view_middleware:process_view方法的列表
_response_middleware:process_response方法的列表
_exception_middleware:process_exception方法的列表
Django的中間件是在其配置文件(settings.py)的MIDDLEWARE_CLASSES元組中定義的。在MIDDLEWARE_CLASSES中踢故,中間件組件用字符串表示:指向中間件類名的完整Python路徑文黎。例如
`MIDDLEWARE_CLASSES = [
? ? 'django.middleware.security.SecurityMiddleware',
? ? '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項(xiàng)目的安裝并不強(qiáng)制要求任何中間件,如果你愿意殿较,MIDDLEWARE_CLASSES可以為空耸峭。中間件出現(xiàn)的順序非常重要:在request和view的處理階段,Django按照MIDDLEWARE_CLASSES中出現(xiàn)的順序來(lái)應(yīng)用中間件淋纲,而在response和exception異常處理階段劳闹,Django則按逆序來(lái)調(diào)用它們。也就是說(shuō),Django將MIDDLEWARE_CLASSES視為view函數(shù)外層的順序包裝子:在request階段按順序從上到下穿過(guò)本涕,而在response則反過(guò)來(lái)业汰。以下這張圖可以更好地幫助你理解:
URLConf(URL映射)
如果處理request的中間件都沒(méi)有直接返回response,那么Django會(huì)去解析用戶請(qǐng)求的URL偏友。URLconf就是Django所支撐網(wǎng)站的目錄蔬胯。它的本質(zhì)是URL模式以及要為該URL模式調(diào)用的視圖函數(shù)之間的映射表。通過(guò)這種方式可以告訴Django位他,對(duì)于這個(gè)URL調(diào)用這段代碼氛濒,對(duì)于那個(gè)URL調(diào)用那段代碼。具體的鹅髓,在Django項(xiàng)目的配置文件中有ROOT_URLCONF常量舞竿,這個(gè)常量加上根目錄"/",作為參數(shù)來(lái)創(chuàng)建django.core.urlresolvers.RegexURLResolver的實(shí)例窿冯,然后通過(guò)它的resolve方法解析用戶請(qǐng)求的URL骗奖,找到第一個(gè)匹配的view。
有關(guān)urlconf的內(nèi)容醒串,大家可以參考 [理解curlConf]()
Template(模板)
大部分web框架都有自己的Template(模板)系統(tǒng)执桌,Django也是。但是芜赌,Django模板不同于Mako模板和jinja2模