1: 关于路由
# django中, 路由的写法有很多,从最早一点几版本的 url(xxxxx) 的形式到后面re_path(xxxx) ,以及参考flask的 path(xxxx) 的格式。
# 无论是哪种 ,实现的功能本质上就是,匹配url和对应的额视图函数,换言之,就是,找到用户访问的url对应的视图函数,并且执行它。
# 下面是urls.py的示范:
# urls.py
from django.contrib import admin
from django.http import HttpResponse
from django.urls import path, URLPattern
from django.urls.resolvers import RoutePattern
from django.urls import reverse
def user(request):
print(request.resolver_match)
return HttpResponse("user页面")
urlpatterns = [
path("user/",user), # 来一个简单的url,其绑定的视图函数就在当前文件意思意思即可。
]
2:找到django程序请求的入口
# wsgi.py
#
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_wsgi_application()
这里的 application = get_wsgi_application()
其实就是创建了一个web应用程序的wsgi对象,作为处理web服务器发送的http请求的入口。至此,他会处理好web服务器和web应用程序之间的
可以这么比喻: web服务器 就是酒店大堂,客户来了,需要进入大堂,同时需要一个接待员,这个接待员就是我们的get_wsgi_application()对象
#点进 get_wsgi_application() ,发现它会返回一个WSGIHandler()
# core\wsgi.py
def get_wsgi_application():
django.setup(set_prefix=False)
return WSGIHandler()
# 继续点入: 发现这是一个类,那么 之前的get_wsgi_application
#其实是在执行WSGIHandler() ,而WSGIHandler是一个类,把一个类用函数的
# 格式进行调用,那么该类的__cell__ 方法会被调用
# core\handlers\wsgi.py
class WSGIHandler(base.BaseHandler):
(略)
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = "%d %s" % (response.status_code, response.reason_phrase)
response_headers = [
*response.items(),
*(("Set-Cookie", c.output(header="")) for c in response.cookies.values()),
]
start_response(status, response_headers)
if getattr(response, "file_to_stream", None) is not None and environ.get(
"wsgi.file_wrapper"
):
# If `wsgi.file_wrapper` is used the WSGI server does not call
# .close on the response, but on the file wrapper. Patch it to use
# response.close instead which takes care of closing all files.
response.file_to_stream.close = response.close
response = environ["wsgi.file_wrapper"](
response.file_to_stream, response.block_size
)
return response
request = self.request_class(environ)
这一句的environ的作用就是把请求相关的参数,类似于请求头,请求体封装到request 里 。
response = self.get_response(request)
注意到这个WSGIHandler 最后会返回response,所以这句也非常之重要,它通过执行get_response 方法,来生成response ,所以我们需要再次进入到get_response 方法进行分析
getattr
这一反射 也非常重要,他会在response找到指定的字符串的属性或者方法,
先去了解get_response 方法在分析这个反射。先去看看这个get_response 会 拿到一个啥玩意。
#core\handlers\base.py
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) # 本质上就是执行了 handler(request) 因为 self._middleware_chain = handler
# handler(request) 最后找到了 ._get_response 方法
response._resource_closers.append(request.close)
if response.status_code >= 400:
log_response(
"%s: %s",
response.reason_phrase,
request.path,
response=response,
request=request,
)
return response
会发先,get_response 也是最后返回response ,它也没有生成的逻辑,而是通过, response = self._middleware_chain(request)
进行生成的。所以还是需要继续进行深入_middleware_chain 方法看看它生成response的逻辑
#core\handlers\base.py
# 追踪_middleware_chain方法,结果发现 _middleware_chain 来自于handler 方法。
self._middleware_chain = handler
# 往上几行发现 这句代码,但是这段代码其实是在判断是否同步,同步的话依旧是返回handler 因此还需要继续往上找
handler = self.adapt_method_mode(is_async, handler, handler_is_async)
# 中间还有很多个对handler进行转发或者包装的操作,找到handler第一次出现的地方,大约37行
get_response = self._get_response_async if is_async else self._get_response # 异步为_get_response_async ,同步为 _get_response
handler = convert_exception_to_response(get_response)
至此。,经过分析,找到了这个handler的真正出处,他就是决定response的关键,
convert_exception_to_response
的作用其实就是接收一个方法/对象,进行类似于异常捕获的操作,本质上还是get_response 方法。接下来就是追踪get_response 方法
# 因为我们仅考虑同步请求,所以:
get_response = self._get_response_async if is_async else self._get_response
代码中会执行self._get_response 方法,继续追踪:
# core\handlers\base.py 中约17 行
def _get_response(self, request):
response = None
callback, callback_args, callback_kwargs = self.resolve_request(request)
# self.resolve_request(request)会触发路由匹配的动作 ,会返回三个值,callback 接收为视图函数, 后面两个表示视图函数的参数
# 所以继续追踪resolve_reuqest 方法,
def resolve_request(self, request):
# Work out the resolver.
if hasattr(request, "urlconf"):
urlconf = request.urlconf
set_urlconf(urlconf)
resolver = get_resolver(urlconf)
else:
resolver = get_resolver()
# Resolve the view, and assign the match object back to the request.
# 1 :resolver = get_resolver() resolver是什么呢?????
# 创建了一个对象 resolver = URLResolver(RegexPattern(r"^/"), "mysite.urls") # urlconf 其实就是配置文件中的"项目名.urls" 这一字符串
# 2 : resolver.resolve 通过该方法进行路由匹配
#
# 3 : resolver_match 即为匹配之后的返回值,并且随后就赋值给request对象的 request.resolver_match 属性值
resolver_match = resolver.resolve(request.path_info) # resolve 方法其实就是在匹配url了, request.path_info 就是我们url中端口号后面的/xx/xxx/xxxx/
request.resolver_match = resolver_match # 此时resolver_match 其实就是路由匹配成功的结果
return resolver_match属性中。这个匹配结果就包含我们写的url对应的回调函数及对应的参数。
hasattr(request, "urlconf")
,主要是判断request这一个对象是否有urlconf这一属性,如果没有就会引用 配置文件中的ROOT_URLCONF
的值作为urlconf ,来创建url解析器 resolver,如果有就直接引用urlconf 创建一个url解析器 resolver
resolver_match = resolver.resolve(request.path_info)
通过调用url解析器的resolve方法来匹配用户请求的url,request.path_info
表示的就是用户访问的url,比如 “/user/info/” 。最后将匹配结果保存在request对象的resolver_match属性中。这个匹配结果就包含我们写的url对应的回调函数及对应的参数。
所以之前的callback, callback_args, callback_kwargs = self.resolve_request(request)
将会接收到视图函数以及请求参数
而解析器的resolve方法长啥样呢,我们需要继续深入
# 这段是创建解析器的关键
if hasattr(request, "urlconf"):
urlconf = request.urlconf
set_urlconf(urlconf)
resolver = get_resolver(urlconf)
else:
resolver = get_resolver()
# 点进get_resolve
def get_resolver(urlconf=None):
if urlconf is None:
urlconf = settings.ROOT_URLCONF
return _get_cached_resolver(urlconf)
# 然后点进_get_cached_resolver 方法
@functools.lru_cache(maxsize=None)
def _get_cached_resolver(urlconf=None):
return URLResolver(RegexPattern(r"^/"), urlconf)
原来,最后解析器其实是一个 URLResolver 对象,找到这个对象的resolve方法
def resolve(self, path):
path = str(path) # path may be a reverse_lazy object
tried = []
match = self.pattern.match(path) # 这里其实是在匹配url前面的/ 也就是8000 后面的/
if match: #
new_path, args, kwargs = match
for pattern in self.url_patterns: # 遍历 self.url_patterns 就是 urls.py 里面的 urlpattern列表中所有的路由
try:
sub_match = pattern.resolve(new_path) # 每一个path其实本质上返回的是一个URLPattern对象,所以遍历到pattern执行的是URLPattern类的resolve方法。
略....
他的本质就是这段for pattern in self.url_patterns:
,这是在遍历urls文件中的urlpattern列表,也就是编写路由的那个列表。注意,它对遍历到的每一个元素,调用了。resolve方法, 这个resolve方法是路由的path对象的方法。sub_match = pattern.resolve(new_path)
path("xx/xxx",views.xxx)
的本质其实也是创建了一个URLPattern对象,
class URLPattern:
......
def resolve(self, path): # path就是 "user/"
match = self.pattern.match(path)
if match:
new_path, args, captured_kwargs = match
# Pass any default args as **kwargs.
kwargs = {**captured_kwargs, **self.default_args}
return ResolverMatch(
self.callback,
args,
kwargs,
self.pattern.name,
route=str(self.pattern),
captured_kwargs=captured_kwargs,
extra_kwargs=self.default_args,
)
因此可以判断
pattern.resolve(new_path) 的结果其实就是
ResolverMatch(
self.callback,
args,
kwargs,
self.pattern.name,
route=str(self.pattern),
captured_kwargs=captured_kwargs,
extra_kwargs=self.default_args,
)
也就是callback, callback_args, callback_kwargs = self.resolve_request(request) 的结果,所以前三个参数就是匹配到的回调函数和参数
最后只要匹配成功就启动这个回调函数即可。