首页 > 编程语言 >Django中关于路由匹配的源码分析

Django中关于路由匹配的源码分析

时间:2023-10-13 09:02:44浏览次数:38  
标签:get self request Django 源码 resolver path response 路由

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) 的结果,所以前三个参数就是匹配到的回调函数和参数
最后只要匹配成功就启动这个回调函数即可。

标签:get,self,request,Django,源码,resolver,path,response,路由
From: https://www.cnblogs.com/yu-xin666/p/17760801.html

相关文章

  • django 结合rpc服务传输
    1概述2基础依赖3定义服务和消息4生成gRPC代码5创建服务和客户端服务6启动服务端和客户端7Django中集成gRPC8安全认证方面9健康检测10相关文档生成概述gRPC(gRPCRemoteProcedureCall)是一种高性能、跨语言的远程过程调用(RPC)框架,由Google开发并......
  • SRM供应商招采系统(源码)
    前言:随着互联网和数字技术的不断发展,企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式,能够提高采购效率、降低采购成本、优化供应商合作效率,已成为企业实现效益提升的关键手段。系统获取在文末。一、建设的必要性数字化采购平台的应用具有以下优......
  • javaweb第8章源码
    javaweb第8章源码下载链接:https://wwpv.lanzoue.com/iRXto1bmbtqb文件结构CHAPTER08│.classpath│.project│├─.settings│.jsdtscope│org.eclipse.jdt.core.prefs│org.eclipse.wst.common.component│org.eclipse.wst.common.projec......
  • 直播带货系统源码解析:搭建你自己的电商直播APP
    通过电商直播,企业和个体经营者能够与潜在客户实时互动,展示产品,提供实时解答,以及鼓励销售。正因如此,一个强大的直播带货系统源码是必不可少的。本文将深入探讨如何使用直播带货系统源码来搭建你自己的电商直播APP。第一部分:理解直播带货系统电商直播是一种创新的销售策略,它结合了传......
  • 动物识别系统python+Django网页界面+TensorFlow算法模型+数据集训练
    一、简介动物识别系统。基于Python+TensorFlow+Django网页框架+ResNet50算法模型实现实现步骤如下:收集多种动物的图片数据集,并整理归类然后使用TensorFlow搭建ResNet50算法模型网络对数据集进行多次迭代训练最后得到一个精度较高的H5模型文件基于训练好的模型,使用Django开......
  • 成品直播源码,图片放大且有渐变色罩层出现
    成品直播源码,图片放大且有渐变色罩层出现 <!DOCTYPEhtml><html><head><metacharset="utf-8"><title>5</title><style>.mask1,.text{position:relative;overflow:hidden;width:800px;height:500px;transition:all.5s;}      ......
  • C# +.Net +Oracle的医院化验室LIS系统源码
    LIS系统源码技术细节:Ø体系结构:Client/Server架构SaaS模式Ø客户端:WPF+WindowsFormsØ服务端:C#+.NetØ数据库:OracleØ接口技术:RESTfulAPI+Http+WCFLIS检验系统一种专门用于医院化验室的计算机系统,它致力于提高医院化验室的工作效率和检测准确率。LIS系统由多个子系统组......
  • 交通标志识别系统python+TensorFlow+算法模型+Django网页+数据集
    一、介绍交通标志识别系统。技术涉及:Python编程语言开发TensorFlow搭建算法模型对数据集进行训练得到一个精度较高的模型文件Django开发网页端界面平台实现对58种交通标志图片进行识别二、效果图片展示三、演示视频and代码视频+代码+介绍:https://s7bacwcxv4.feishu.......
  • 编程式导航——两种路由跳转方式
    编程式导航:通过JS的方式实现路由跳转如何实现点击按钮跳转? 一、不传参:1.通过path路径跳转(简易方便)①简写:  按钮的点击事件中写  this.$router.push('/路由路径')    比如:this.$router.push('/search') ②完整写法:this.$router.push({ ......
  • javaweb第7章源码
    javaweb第7章源码:下载链接:https://wwpv.lanzoue.com/iurOS1bijocb文件结构:CHAPTER07│.classpath│.project│├─.settings│.jsdtscope│org.eclipse.jdt.core.prefs│org.eclipse.wst.common.component│org.eclipse.wst.common.proje......