【Django源碼淺析】—Django runserver啓動流程與URL路由

新的一篇,大概梳理一下django的url路由流程與runserver的啓動流程。python

小公舉小姐姐們,求贊求轉發,要抱抱django

分析代碼首先找到分析入口,根據由入口開始的處理流程,定位到要分析的節點。app

通常調試運行django使用manager命令:less

根據上一回的django命令系統的分析函數

找到runserver命令(django/core/management/commands/runserver.py)this

runserver命令調用鏈爲self.handle->self.run->self.inner_run->runurl

run函數(servers/basehttp.py):spa

分析run函數,定義了一個httpd_cls類,繼承WSGIServer以及ThreadingMinIn,以後初始化,調用serve_forever函數開始監聽端口。調試

監聽端口接收請求以後會交由_handle_request_noblock函數處理,最終請求會發送到上圖run函數中的WSGIRequestHandler類處理:code

RequestHandlerClass就是run方法中的WSGIRequestHandler,WSGIRequestHandler的基類__init__函數會自動調用self.handle方法。

那接着來看一下self.handle:

self.handle調用ServerHandler的run方法,self.server.get_app()獲得的就是basehttp.py 中run函數中httpd.set_app(wsgi_handler)中的wsgi_handler,即core/wsig.py中的get_wsig_application函數(此時執行django.setup函數,作app加載),見下圖

至此python manager.py runserver 0:80命令啓動server的工做基本完成了。在server接到request請求以後會自動將請求交給WSGIHandle類(注:根據wsgi協議,WSGIHandler是callable的,WSGIHandler在初始化的時候執行了load_middleware函數,加載中間件)處理。經過WSGIHandle的get_response函數相應request請求。

def get_response(self, request):
    """Return an HttpResponse object for the given HttpRequest."""
    # Setup default url resolver for this thread
    set_urlconf(settings.ROOT_URLCONF)

    response = self._middleware_chain(request)

    # This block is only needed for legacy MIDDLEWARE_CLASSES; if
    # MIDDLEWARE is used, self._response_middleware will be empty.
    try:
        # Apply response middleware, regardless of the response
        for middleware_method in self._response_middleware:
            response = middleware_method(request, response)
            # Complain if the response middleware returned None (a common error).
            if response is None:
                raise ValueError(
                    "%s.process_response didn't return an "
                    "HttpResponse object. It returned None instead."
                    % (middleware_method.__self__.__class__.__name__))
    except Exception:  # Any exception should be gathered and handled
        signals.got_request_exception.send(sender=self.__class__, request=request)
        response = self.handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())

    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

get_response函數第一行就開始加載setting中的urlconfig,緊接着調用

response = self._middleware_chain(request)處理請求,因此可見,相應request請求的諸多操做都是經由一個個middleware處理以後的來的,那麼猜一下就知道,加載url路由響應請求天然應該是在加載middleware的操做中完成的了。跟蹤代碼找到加載middleware函數的位置,最後定位到WSGIHandler的_get_response函數,能夠發現其中的這幾行代碼:

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

resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match

日後繼續讀不難發現,callback函數就是咱們在view模塊中定義的響應函數,用來相應客戶端的請求。因此天然能夠推斷出get_resolver()函數是獲取路由解析類的,而resolver_match則是根據用戶請求獲取的路由結果,callback則是針對request.path_info的相應函數。

路由加載:

@lru_cache.lru_cache(maxsize=None)
def get_resolver(urlconf=None):
    if urlconf is None:
        from django.conf import settings
        urlconf = settings.ROOT_URLCONF
    return RegexURLResolver(r'^/', urlconf)

路由:

def resolve(self, path):
    path = force_text(path)  # path may be a reverse_lazy object
    tried = []
    match = self.regex.search(path)
    if match:
        new_path = path[match.end():]
        for pattern in self.url_patterns:
            try:
                sub_match = pattern.resolve(new_path)
            except Resolver404 as e:
                sub_tried = e.args[0].get('tried')
                if sub_tried is not None:
                    tried.extend([pattern] + t for t in sub_tried)
                else:
                    tried.append([pattern])
            else:
                if sub_match:
                    # Merge captured arguments in match with submatch
                    sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
                    sub_match_dict.update(sub_match.kwargs)

                    # If there are *any* named groups, ignore all non-named groups.
                    # Otherwise, pass all non-named arguments as positional arguments.
                    sub_match_args = sub_match.args
                    if not sub_match_dict:
                        sub_match_args = match.groups() + sub_match.args

                    return ResolverMatch(
                        sub_match.func,
                        sub_match_args,
                        sub_match_dict,
                        sub_match.url_name,
                        [self.app_name] + sub_match.app_names,
                        [self.namespace] + sub_match.namespaces,
                    )
                tried.append([pattern])
        raise Resolver404({'tried': tried, 'path': new_path})
    raise Resolver404({'path': path})

resolve函數解析request.path_info請求,match爲匹配結果,若是匹配失敗則拋出404異常,若是匹配成功則進行下一步的url解析,匹配剩餘url字符(new_path = path[match.end():]),以後的代碼爲相似循環嵌套調用(for pattern in self.url_patterns),不斷嘗試匹配url_patterns中的正則規則,若是最終沒有匹配項,拋出404異常,若是最終匹配成功,則返回ResolverMatch實例:

注意:self.url_patterns多是兩種值,一種是RegexURLResolver,另一種是RegexURLPattern。

if sub_match:
    # Merge captured arguments in match with submatch
    sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
    sub_match_dict.update(sub_match.kwargs)

    # If there are *any* named groups, ignore all non-named groups.
    # Otherwise, pass all non-named arguments as positional arguments.
    sub_match_args = sub_match.args
    if not sub_match_dict:
        sub_match_args = match.groups() + sub_match.args

    return ResolverMatch(
        sub_match.func,
        sub_match_args,
        sub_match_dict,
        sub_match.url_name,
        [self.app_name] + sub_match.app_names,
        [self.namespace] + sub_match.namespaces,
    )

ResolverMatch中的sub_match.func即爲view層的響應函數,至此路由完成。

相關文章
相關標籤/搜索