当前位置: 首页 > news >正文

django之请求处理过程分析

在开发过程中,可能会遇到一些框架的报错,比如http方法不允许、中间件检查不通过、路由匹配错误等,此类情况下因为报错是框架层面报的,这时候就有必要在框架的源代码层面打断点从而定位问题了。而源代码层面的断点打在哪,就需要了解清楚本文说的一些流程。

1、服务启动过程

我们最熟悉的服务启动方式就是这个runserver了:

python3 manange.py runserver

先讲下runserver的代码内容。

在django的源码里,有这样一个目录django/core/managemtn/commands/,这个目录下有很多我们熟悉的文件,比如runserver.py、makemigrations.py、migrate.py等。

这些就是python3 manange.py --help显示的命令行对应的处理程序。

我们也可以在这里增加自定义的脚本,从而实现自定义命令。

我们看下runserver.py里的handle函数,这是每个命令的入口函数。

# 启动过程中的函数调用
def handle()self.run()self.inner_run()handler = self.get_handler()run(handler)    # 这里的run是引自django.core.servers.basehttp  # 先看下这个run()函数
# django/core/servers/basehttp.py
def run(addr,port,wsgi_handler):# 绑定IP、端口server_address = (addr, port)# 初始化http服务器httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)# 设置请求处理的函数httpd.set_app(wsgi_handler)# 启动httpd.serve_forever()# 其中这个请求处理的函数handler就是self.get_handler()获取的。def get_handler()get_internal_wsgi_application()from django.conf import settingsapp_path = getattr(settings, "WSGI_APPLICATION")return import_string(app_path)
# 这个handler是在setting文件里通过WSGI_APPLICATION参数指定的。# 比如我项目的setting文件里
WSGI_APPLICATION = "application.wsgi.application"
# 这是一个路径,可以通过import导入的,从项目根目录下顺着找,找到这样一个文件# {项目根目录}/application/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
application = get_wsgi_application()
# 在我的项目里没有做过多自定义,调用django提供的get_wsgi_application,
# 这个函数返回的内容就是handler了# django/core/wsgi.py
def get_wsgi_application():return WSGIHandler() # 至此,我们找到了handler的真面目,就是这个WSGIHandler()

上面只讲了下django自带的runserver的启动过程,在生产环境下,我们会看到用比如uwsgi、gunicorn等命令来启动服务,其实原理都差不多,最主要的就是给这些http服务传递一个请求的入口函数。

再扩展一下,django其实是支持两类服务器,同步服务器(wsgi)和异步服务器(asgi),我们上面介绍的uwsgi这些都是同步服务器,异步服务器比如uvicorn,此处就不介绍了,感兴趣自行研究。

2、请求处理过程

浏览器发起一个http请求,最后如何转到我们写的一个view函数上呢,继续挖一挖。

首先请求的处理函数是这个WSGIHandler()。

# django.core.handlers.wsgi.py
# WSGIHandler()这个类继承自BaseHandler
# 其自身的代码很少,主要的几个方法都在BaseHandler里class WSGIHandler(base.BaseHandler):# 定义一个初始化HttpRequest的类,# 这个类的作用就是将http请求封装成django标准的HttpRequest,方便后续使用request_class = WSGIRequest# 初始化方法# 调用self.load_middleware()加载setting文件里定义的中间件def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.load_middleware()# 请求到达时进入__call_函数处理# 先用request_class把http请求封装成HttpRequest# 具体的处理逻辑就在BaseHandler的get_response函数里了def __call__(self, environ, start_response):request = self.request_class(environ)response = self.get_response(request)return response# django.core.handlers.base.py
# 下面就是BaseHandler的get_response函数
# 通过调用self._middleware_chain函数,拿到响应结果
def get_response(self, request):set_urlconf(settings.ROOT_URLCONF)response = self._middleware_chain(request)# 这个self._middleware_chain其实就是_get_response
# 两者是在load_middleware函数里绑定的,主要还是区分同步和异步,我们本篇只说同步服务器的情况
# get_response = self._get_response_async if is_async else self._get_response# 核心的处理函数_get_response
def _get_response(self, request):# resolve_request:解析路由,获得请求对应的处理函数callback, callback_args, callback_kwargs = self.resolve_request(request)# 先依次应用中间件里的处理for middleware_method in self._view_middleware:response = middleware_method(request, callback, callback_args, callback_kwargs)# 再执行callback处理函数,就是我们写的view视图函数wrapped_callback = self.make_view_atomic(callback)response = wrapped_callback(request, *callback_args, **callback_kwargs)# 返回处理结果return response# 关于resolve_request如何通过路由表找到对应视图函数的解析,可以参考本系列的路由相关内容

至此,http请求的处理过程基本清晰了,核心逻辑在_get_response中,主要包含解析路由表–应用中间件–执行视图函数–返回结果这几个步骤。
所以后续再遇到比如路由不到自己预想的函数,就可以跟一下resolve_request等等。

3、django中的HttpRequest和HttpResponse

在django框架中,请求在内部处理的流程如下:

  1. 客户端发送请求 → Django 创建 HttpRequest 对象,封装所有请求数据。
  2. 视图函数处理 → 开发者通过 request 获取数据,处理后生成 HttpResponse
  3. 服务器返回响应 → Django 将 HttpResponse 转换为符合 HTTP 协议的响应,发送给客户端。

为什么会出现HttpRequestHttpResponse呢?

主要是为了避免开发者处理底层协议细节,简化数据访问。

如果没有它们,开发者需要手动解析原始 HTTP 数据,代码会变得复杂且难以维护。

学习这两个类的目的是什么呢?

对于HttpRequest,主要是为了取出请求里的数据,比如header信息、表单信息、参数信息等,因此要搞清楚这些信息存在HttpRequest的哪些属性里。

对于HttpResponse,因为是开发者要封装的,要搞清楚如何将自己要返回的数据给到HttpResponse。

HttpRequest

httpReqeust的属性有:

属性类型说明
methodstrHTTP 请求方法(GET/POST/PUT/DELETE等)
GETQueryDict包含所有GET参数的类字典对象
POSTQueryDict包含所有POST参数的类字典对象
FILESMultiValueDict包含所有上传文件的类字典对象
COOKIESdict包含所有cookies的字典
METAdict包含所有HTTP头信息的字典
pathstr请求的完整路径(不包括域名和查询参数)
path_infostr类似于path,但在某些服务器配置下可能不同
bodybytes原始HTTP请求体(字节字符串)
headersHttpHeadersDjango 3.2+引入,更友好的headers访问方式
sessionSessionStore可读写的类字典对象,表示当前会话
userUser表示当前登录用户的User对象

属性说明:

1、取参数,get方法的参数和post方法的参数分别存在request.GET和request.POST中,后端在取参数的时候要对判断http方法。

def example_view(request):if request.method == "POST":username = request.POST.get("username")  # 获取 POST 参数file = request.FILES.get("avatar")       # 获取文件elif request.method == "GET":username = request.GET.get("username")

2、特殊情况下,取参需要使用body中的内容,注意body属于原始字符串,后端使用前需要解码。

import json
data = json.loads(request.body.decode('utf-8'))

3、META部分包含了所有HTTP头信息,请求中的任何 HTTP 头都会被转换为 META 键,方法是将所有字符转换为大写字母,用下划线代替任何连字符,并在名称前加上 HTTP_ 前缀,例如,一个名为 X-Name 的头将被映射到 META 键 HTTP_X_NAME。

在这里插入图片描述

def example_view(request):name = request.META['HTTP_X_NAME']

httpReqeust还提供了一些获取常用属性的方法,比如客户端的主机名、端口等

方法返回类型说明
get_full_path()str返回path + 查询字符串
is_secure()bool如果请求是通过HTTPS发出的返回True
get_host()str获取请求的主机名
get_port()str获取请求的端口号
build_absolute_uri(location)str返回location的绝对URI
get_signed_cookie(key)str获取签名cookie的值
read(size=None)bytes从请求体中读取数据

httpResponse

学习httpResponse就是搞清楚如何构造一个django支持的响应。先看这个类的构造函数。

# HttpResponse是在view处理阶段要自行构造的,因此需要搞清楚要传的参数
class HttpResponse(HttpResponseBase):def __init__(self,content=b'',content_type=None,status=None,reason=None,charset=None):

可以看出我们要返回的内容主要是放在content属性里,以字节的格式返回。

def my_view(request):return HttpResponse('ok')# 最常见的比如就是把'ok'赋值给content返回。

除了上面说的简单的字符串,HttpResponse还支持以下属性设置。

属性类型说明
contentbytes响应内容(字节形式)
charsetstr响应编码(如 ‘utf-8’)
status_codeintHTTP 状态码(如 200, 404)
reason_phrasestr状态码描述(如 “OK”, “Not Found”)
headersHttpHeaders响应头(类字典对象)
cookiesSimpleCookie用于设置 Cookie 的对象

还可以通过内置的函数,进行属性设置和数据写入。

方法说明
set_cookie()设置 Cookie
delete_cookie()删除 Cookie
write(content)写入响应内容(用于流式响应)
setdefault(key, value)设置默认响应头

HttpResponse常用的子类如下,主要是对特定场景做了定制化,减少开发工作量。

子类说明
JsonResponse返回 JSON 响应(自动设置 Content-Type)
FileResponse返回文件下载响应
StreamingHttpResponse流式响应(大文件处理)
HttpResponseRedirect302 重定向响应
HttpResponsePermanentRedirect301 永久重定向
HttpResponseNotFound404 响应
HttpResponseForbidden403 响应
HttpResponseBadRequest400 响应

4、DRF中的Request和Response

Request

在DRF中,基于httpReqeust做了扩展,封装成了Requests类。

从DRF的Request获取数据和django的HttpRequest获取数据有哪些区别:

1、data属性,支持所有的文件输入和非文件输入,支持除POST之外的HTTP方法的内容,这意味着你可以访问PUT和PATCH请求的内容。(在djanog中读取文件需要读取request.FILES,非文件需要读取request.GET或request.POST或request.body)。

2、query_params属性,就是django中的request.GET。

3、增加了认证支持(此处不展开)。

DRF是在请求的哪个步骤引入了Request呢?

# 首先我们知道请求经过self.resolve_request(request)解析出了处理函数。
# 对于drf中基于类的视图中,通常会调用类的as_view()函数
# 而as_view函数返回的就是该类的dispatch()函数。
# 也就是resolve_request之后的函数处理入口就是这个类的dispatch函数。# rest_framework/views.py
class APIView(View):def dispatch(self, request, *args, **kwargs):request = self.initialize_request(request, *args, **kwargs)def initialize_request(self, request, *args, **kwargs):parser_context = self.get_parser_context(request)return Request(request,parsers=self.get_parsers(),authenticators=self.get_authenticators(),negotiator=self.get_content_negotiator(),parser_context=parser_context)
# 从上面代码中可以看出,Request是在dispatch函数中引入的。
# 而DRF中的viewset、modelViewSet都是继承自APIView,都是复用的这部分代码。
# 所以在DRF中只要你的视图是基于APIView、viewset、modelViewSet,
# 那么你用的request大概率就是Request实例,否则就还是django的httpRequest.

Response

对于DRF的响应类Response,如何构造返回实例。

class Response(SimpleTemplateResponse)def __init__(self, data=None, status=None,template_name=None, headers=None,exception=False, content_type=None):

可以看出数据主要赋值给data进行返回。data要求是序列化后的数据,不能是类实例。

相关文章:

  • 得物GO面试题及参考答案
  • 函数调用(Function Calling)
  • 中电金信:从智能应用到全栈AI,大模型如何重构金融业务价值链?
  • OS问题:什么是进程,进程的通信方式有哪些
  • 【Linux 】centos8搭建nextcloud全过程
  • 软件工程专业本科毕业论文模板
  • 52+Nand 编译hello_world 工程烧录后,上电后无log
  • Web自动化测试:如何生成高质量的测试报告?
  • 【更新中】(文档+代码)基于推荐算法和Springboot+Vue的购物商城
  • React 性能监控与错误上报
  • Unity 中实现可翻页的 PageView
  • RP2040慎用CriticalSection
  • 思维力三阶 · 序章:从认知碎片到系统思维——点亮内心的“认知操作系统”蓝图
  • 江科大睡眠,停止,待机模式hal库实现
  • 长参考帧LTR
  • [Java 基础]面向对象-多态
  • C语言到底使用什么编码
  • git删除本地分支和远程分支
  • 【力扣】3403. 从盒子中找出字典序最大的字符串 I
  • 2025年6月4日收获
  • dedecms做网站怎么查看/广州竞价托管公司
  • 黄村网站建设一条龙/小网站搜什么关键词
  • 如何在工商局网站做企业年报/seo关键字优化
  • 广州网站建设 易点/虚拟主机搭建网站
  • 衢州做网站公司/海南百度推广代理商
  • 移动端网站开发语言/seo还有哪些方面的优化