博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Django源码浅析】—Django runserver启动流程与URL路由
阅读量:6904 次
发布时间:2019-06-27

本文共 6228 字,大约阅读时间需要 20 分钟。

hot3.png

新的一篇,大概梳理一下django的url路由流程与runserver的启动流程。

183742_Eb8l_2504425.jpeg小公举小姐姐们,求赞求转发,要抱抱

分析代码首先找到分析入口,根据由入口开始的处理流程,定位到要分析的节点。

一般调试运行django使用manager命令:

132024_ka8M_2504425.png

根据上一回的django命令系统的分析

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

runserver命令调用链为self.handle->self.run->self.inner_run->run

run函数(servers/basehttp.py):

132921_FBNP_2504425.png

分析run函数,定义了一个httpd_cls类,继承WSGIServer以及ThreadingMinIn,之后初始化,调用serve_forever函数开始监听端口。

监听端口接收请求之后会交由_handle_request_noblock函数处理,最终请求会发送到上图run函数中的WSGIRequestHandler类处理:

134540_aHoc_2504425.png

RequestHandlerClass就是run方法中的WSGIRequestHandler,WSGIRequestHandler的基类__init__函数会自动调用self.handle方法。

那接着来看一下self.handle:

142855_bM4H_2504425.png

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加载),见下图

143214_yUn8_2504425.png

至此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_matchrequest.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层的响应函数,至此路由完成。

 

转载于:https://my.oschina.net/u/2504425/blog/909018

你可能感兴趣的文章
MySQL常用命令大全
查看>>
查看Android应用签名信息
查看>>
Solr官方文档翻译-About & Getting Started
查看>>
MVC、MVP、MVVM、Angular.js、Knockout.js、Backbone.js、React.js、Ember.js、Avalon.js、Vue.js 概念摘录...
查看>>
优化中的subgradient方法
查看>>
fastDFS 一二事 - 简易服务器搭建(单linux)
查看>>
Elasticsearch初步使用(安装、Head配置、分词器配置)
查看>>
thinkphp 3.2.3 计划任务具体实现实例教程
查看>>
php源码解读
查看>>
linux编译ruby1.8.7 出现OPENSSL错误
查看>>
设计模式(五):中介者模式
查看>>
grep(Global Regular Expression Print)
查看>>
WCF学习之旅—WCF服务的批量寄宿(十三)
查看>>
解决“不是有效的win32应用程序”问题
查看>>
安装opencv以及遇到的坑
查看>>
C# 匿名函数
查看>>
MySQL锁系列2 表锁
查看>>
Lua中的closure(闭合函数)
查看>>
一个int类型引发的bug
查看>>
js 片段 - 控制类型为 text 的 input 类型
查看>>