django源碼分析之請(qǐng)求響應(yīng)流程

web應(yīng)用或者網(wǎng)站本質(zhì)上都是圍繞著請(qǐng)求-響應(yīng)的方式來運(yùn)作的。當(dāng)你通過瀏覽器訪問網(wǎng)站時(shí),瀏覽器會(huì)向web服務(wù)器發(fā)送請(qǐng)求。當(dāng)web服務(wù)器收到請(qǐng)求后,服務(wù)器會(huì)對(duì)請(qǐng)求進(jìn)行相應(yīng)的處理,然后返回相應(yīng)的響應(yīng)給瀏覽器,最后瀏覽器呈現(xiàn)給你。
毫無意外,Django應(yīng)用也是如此。它也需要給用戶發(fā)送的請(qǐng)求返回相應(yīng)的響應(yīng)。接著我將會(huì)通過分析Django框架的代碼來解釋Django的請(qǐng)求響應(yīng)流程是如何的。

代碼分析

Django應(yīng)用最開始的入口處在wsgi.py,這里是從web服務(wù)器轉(zhuǎn)發(fā)請(qǐng)求到Django應(yīng)用的地方,同時(shí)也是Django應(yīng)用返回響應(yīng)給web服務(wù)器的地方。

# django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 加載項(xiàng)目配置的中間件
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        # 根據(jù)web服務(wù)器傳入的參數(shù),初始化request請(qǐng)求
        request = self.request_class(environ)
        # Django開始處理請(qǐng)求,生成響應(yīng)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = list(response.items())
        for c in response.cookies.values():
            response_headers.append(('Set-Cookie', c.output(header='')))
        # wsgi 協(xié)議,調(diào)用start_response,然后給web服務(wù)器
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

上面這段代碼可以看到WSGIHandler是引導(dǎo)Django應(yīng)用運(yùn)用的地方,從這里才開始能真正處理請(qǐng)求以及返回響應(yīng)給web服務(wù)器。具體的操作相關(guān)的代碼在base.BaseHandler,先去base.BaseHandler深入分析self.load_middleware()加載中間件的過程.

#django/core/handlers/base.py
class BaseHandler:
    _request_middleware = None
    _view_middleware = None
    _template_response_middleware = None
    _response_middleware = None
    _exception_middleware = None
    _middleware_chain = None

    def load_middleware(self):
        """
        Populate middleware lists from settings.MIDDLEWARE.

        Must be called after the environment is fixed (see __call__ in subclasses).
        """
        #中間件鏈表,這其中的_request_middleware和_response_middleware已經(jīng)沒有用處,這不表示請(qǐng)求中間件和響應(yīng)中間件沒用,只是用了新的機(jī)制來應(yīng)用
        self._request_middleware = []
        self._view_middleware = []
        self._template_response_middleware = []
        self._response_middleware = []
        self._exception_middleware = []

        #將self._get_response包裝在異常處理內(nèi),此時(shí)的handler是一個(gè)包裝了_get_response的修飾器函數(shù),進(jìn)行了異常處理。
        handler = convert_exception_to_response(self._get_response)
        # 遍歷settings配置文件中的中間件
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            try:
                mw_instance = middleware(handler)
            except MiddlewareNotUsed as exc:
                if settings.DEBUG:
                    if str(exc):
                        logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                    else:
                        logger.debug('MiddlewareNotUsed: %r', middleware_path)
                continue

            if mw_instance is None:
                raise ImproperlyConfigured(
                    'Middleware factory %s returned None.' % middleware_path
                )
            '''
            可以看到,這里只有對(duì)_view_middleware,_template_response_middleware,_exception_middleware這3類中間件的鏈表進(jìn)行了處理。
            '''
            if hasattr(mw_instance, 'process_view'):
                self._view_middleware.insert(0, mw_instance.process_view)
            if hasattr(mw_instance, 'process_template_response'):
                self._template_response_middleware.append(mw_instance.process_template_response)
            if hasattr(mw_instance, 'process_exception'):
                self._exception_middleware.append(mw_instance.process_exception)
            # 是這里對(duì)_request_middleware和_response_middleware進(jìn)行了處理。對(duì)handler進(jìn)行了一層包裝。handler = convert_exception_to_response(middleware(handler))
            handler = convert_exception_to_response(mw_instance)

        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        # 所以這里的_middleware_chain就是一個(gè)修飾器函數(shù),他最內(nèi)層是真正的_get_response,外層則是哪些中間件
        self._middleware_chain = handler

可以看到self.load_middleware()是加載中間件的過程。

可以看到主循環(huán)是對(duì)配置文件里中間件進(jìn)行逆序遍歷,_view_middleware鏈表是執(zhí)行的insert(0操作,所以這個(gè)鏈接最后的順序是和配置文件的順序相同。而_template_response_middleware_exception_middleware都是append的操作,所以最后生成的鏈表順序是和配置文件的順序相反。這里面比較特殊的是_request_middleware_response_middleware,并沒有使用其他3種中間件那樣的鏈表,而是用了包裝器,外層的中間件包裝著里面的中間件,最里面的是_get_response。其實(shí)這兩類中間件都會(huì)繼承自MiddlewareMixin,看完它就一目了然了。

# django/utils/deprecation.py
class MiddlewareMixin:
    def __init__(self, get_response=None):
        self.get_response = get_response
        super().__init__()

    def __call__(self, request):
        response = None
        # 處理request中間件
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        response = response or self.get_response(request)
        # 處理response中間件
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

所以這里我們可以看出大致流程,request中間件和view中間件的處理順序和配置順序相同,另外3種,則是相反的順序。

繼續(xù)看self.get_response(request),看看是如何處理請(qǐng)求,生成響應(yīng)的。

#django/core/handlers/base.py
def get_response(self, request):
    """Return an HttpResponse object for the given HttpRequest."""
    # Setup default url resolver for this thread
    # 根據(jù)settings.ROOT_URLCONF設(shè)置urlconf,初始化路由
    set_urlconf(settings.ROOT_URLCONF)

    # 調(diào)用包裝了self._get_response的_middleware_chain
    response = self._middleware_chain(request)

    response._closable_objects.append(request)

    # If the exception handler returns a TemplateResponse that has not
    # been rendered, force it to be rendered.
    if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
        response = response.render()

    if response.status_code == 404:
        logger.warning(
            'Not Found: %s', request.path,
            extra={'status_code': 404, 'request': request},
        )

    return response

這里是WSGIHandler處理請(qǐng)求調(diào)用的函數(shù),最關(guān)鍵的就是_middleware_chain(request), 他是被中間件包裝了的_get_response的handler。最后看self._get_response.

#django/core/handlers/base.py
def _get_response(self, request):
    """
    Resolve and call the view, then apply view, exception, and
    template_response middleware. This method is everything that happens
    inside the request/response middleware.
    如注釋說的,_get_response是被request/response中間件包裝在最里面的。所以這里面就是實(shí)際調(diào)用具體view的地方了。
    """
    response = None

    if hasattr(request, 'urlconf'):
        urlconf = request.urlconf
        set_urlconf(urlconf)
        resolver = get_resolver(urlconf)
    else:
        resolver = get_resolver()

    # 通過request的url來匹配得到具體的view
    resolver_match = resolver.resolve(request.path_info)
    callback, callback_args, callback_kwargs = resolver_match
    request.resolver_match = resolver_match

    # Apply view middleware
    # 先調(diào)用view中間件
    for middleware_method in self._view_middleware:
        response = middleware_method(request, callback, callback_args, callback_kwargs)
        if response:
            break
    # 這里調(diào)用匹配到的具體的view
    if response is None:
        wrapped_callback = self.make_view_atomic(callback)
        try:
            response = wrapped_callback(request, *callback_args, **callback_kwargs)
        except Exception as e:
            response = self.process_exception_by_middleware(e, request)

    # Complain if the view returned None (a common error).
    if response is None:
        if isinstance(callback, types.FunctionType):    # FBV
            view_name = callback.__name__
        else:                                           # CBV
            view_name = callback.__class__.__name__ + '.__call__'

        raise ValueError(
            "The view %s.%s didn't return an HttpResponse object. It "
            "returned None instead." % (callback.__module__, view_name)
        )

    # If the response supports deferred rendering, apply template
    # response middleware and then render the response
    elif hasattr(response, 'render') and callable(response.render):
        for middleware_method in self._template_response_middleware:
            response = middleware_method(request, response)
            # Complain if the template response middleware returned None (a common error).
            if response is None:
                raise ValueError(
                    "%s.process_template_response didn't return an "
                    "HttpResponse object. It returned None instead."
                    % (middleware_method.__self__.__class__.__name__)
                )

        try:
            response = response.render()
        except Exception as e:
            response = self.process_exception_by_middleware(e, request)

    return response

上面的self._get_response就是通過請(qǐng)求的url匹配具體的view,然后調(diào)用view的過程。通過url匹配具體的view的原理后面再寫一篇文章來介紹。

總結(jié)

通過上面代碼分析,我們已經(jīng)大致了解了Django請(qǐng)求響應(yīng)的流程。大致如下
用戶請(qǐng)求首先會(huì)到web服務(wù)器;
web服務(wù)器會(huì)把請(qǐng)求發(fā)到django.core.handlers.wsgiBaseHandler;
生成request,response,view, exception,template_response中間件鏈表;
按中間件配置順序應(yīng)用request中間件來處理request,如果這中間生成response,則直接返回;
通過urlresolvers.resolve匹配請(qǐng)求的url來找到對(duì)應(yīng)的view;
應(yīng)用view中間件,如果有response,則直接返回;
調(diào)用對(duì)應(yīng)的view,這個(gè)過程和和models進(jìn)行交互,比如從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)等,并渲染模板;
接著response中間件會(huì)被應(yīng)用來處理repsonse;
這其中忽略了一些其他重要的步驟,比如異常中間件的調(diào)用。
最后放一張網(wǎng)上的圖,我覺得畫的比較形象

c0c87634e652807dbebe683a5cc46482.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Refer to: www.threemeal.com/blog/12/ 中間件 中間件是一個(gè)鉤子框架,它們可以介...
    蘭山小亭閱讀 16,782評(píng)論 9 164
  • 目錄 一、中間件簡(jiǎn)介 在django中,中間件其實(shí)就是一個(gè)類,在請(qǐng)求到來和結(jié)束后,django會(huì)根據(jù)自己的規(guī)則在合...
    CaiGuangyin閱讀 883評(píng)論 0 3
  • Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606網(wǎng)站:h...
    布客飛龍閱讀 865評(píng)論 0 37
  • 中間件是一個(gè)鉤子框架,它們可以介入Django 的請(qǐng)求和響應(yīng)處理過程。它是一個(gè)輕量級(jí)、底層的“插件”系統(tǒng),用于在全...
    低吟淺唱1990閱讀 585評(píng)論 0 0
  • 可我想走進(jìn)你的世界 把你帶出痛楚的邊緣
    趙家小q閱讀 127評(píng)論 0 0

友情鏈接更多精彩內(nèi)容