DRF requets源码分析

【四】requets源码分析

【1】查看request传递的数据

(1)视图层

  • 编写传输数据的接口查看request方法的参数
class BookAPIView(APIView):def get(self, request, *args, **kwargs):return Response({'body': request.body, 'data': request.data, 'post': request.POST})def post(self, request, *args, **kwargs):return Response({'body': request.body, 'data': request.data, 'post': request.POST})def put(self, request, *args, **kwargs):return Response({'body': request.body, 'data': request.data, 'post': request.POST})def delete(self, request, *args, **kwargs):return Response({'body': request.body, 'data': request.data, 'post': request.POST})

(2)Postman接口测试

  • 首先是POST方法
    • 可以看到request的data中,无论传输的什么格式数据,最终都是拿到了一个字典格式
    • 可以看到requets的POST中,在发送json格式的数据时是拿不到数据的
    • 可以看到request的body中,无论哪种传输格式,都可以拿到数据,但是具体样式都不统一

image-20240412223225387

  • 然后查看PUT方法
    • 可以看到request的data中,无论传输的什么格式数据,最终都是拿到了一个字典格式
    • 可以看到requets的POST中,在发送json格式的数据时是拿不到数据的
    • 可以看到request的body中,无论哪种传输格式,都可以拿到数据,但是具体样式都不统一

image-20240412223931332

  • 小结:
    • 无论是什么请求方式,还是什么数据格式,都可以在request的data中拿到传输的数据,并且是字典形式
    • 无论是什么请求方式,只要传递的数据编码方式改变了,那么在body中的样式都会不一样
    • request.POST中,有时会拿不到传输的数据

【2】编写装饰器

(1)任务要求

  • 写一个装饰器,装饰视图函数FBV

  • 使得request有data属性,无论哪种编码和请求方式,都是字典

(2)思路和答案

  • 因为无论什么传输格式、无论什么传输方法,body中都可以拿到数据,所以以body为主,处理数据
  • 首先从request.META中拿取传输格式CONTENT_TYPE
  • 然后根据不同的CONTENT_TYPE处理不同的数据
    • json格式直接使用json的反序列化即可
    • urlencoded格式数据,是&分隔多个参数,是=分隔键值对的,所以对数据进行切分处理
    • formdata格式数据,对计算机友好,对开发者极其不友好,输出的样式极其的丑,数据不好切分,所以采用正则处理
def wrapper(func):def inner(request, *args, **kwargs):data = Nonecontent_type = request.META.get('CONTENT_TYPE')if 'json' in content_type:data = json.loads(request.body)elif 'urlencoded' in content_type:data = request.body.decode('utf8')data = {i[0]: unquote(i[-1]) for i in [info.split('=') for info in data.split('&')]}elif 'form-data' in content_type:data = request.body.decode('utf8')pattern = r'Content-Disposition: form-data; name="([^"]+)"\s*\r\n\s*\r\n(.*?)(?=\r\n|--.*?--|\Z)'res = re.findall(pattern, string=data)data = {i[0]: i[1] for i in res}setattr(request, 'data', data)res = func(*args, **kwargs)return resreturn inner

【3】面向对象知识准备

(1)魔法方法__getattr____getattribute__

  • 详情可见:Python 面向对象之魔法方法-CSDN博客

  • __getattribute__ 方法:

    • __getattribute__ 方法会在尝试访问对象的任何属性时被调用,无论这个属性是否存在。
    • 如果覆盖了 __getattribute__ 方法但没有正确地处理所有属性访问,那么即使对于存在的属性也可能导致错误,因为默认的 __getattribute__ 实现会被覆盖。
    • 如果 __getattribute__ 方法引发异常并且没有捕获该异常,那么解释器不会尝试调用 __getattr__ 方法作为后备机制。因此,如果覆盖了 __getattribute__ 方法,需要确保它能正确处理所有可能的属性访问,包括不存在的属性。
  • __getattr__ 方法:

    • __getattr__ 方法仅在 __getattribute__ 方法引发 AttributeError 时被调用,即当尝试访问的属性不存在时。这是 __getattr__ 方法作为后备机制存在的原因。
    • 如果没有覆盖 __getattribute__ 方法,那么 __getattr__ 会在属性不存在时自动被调用。但是,如果覆盖了 __getattribute__ 方法而没有显式地处理不存在的属性,那么 __getattr__ 将不会被调用。
  • 简而言之:

    • __getattribute__总是被调用,无论属性是否存在
    • __getattr__ 仅在 __getattribute__ 失败(即引发AttributeError)并且没有覆盖 __getattribute__ 方法时被调用,用于处理不存在的属性。
class A:class_name = 'A'def __getattr__(self, item):print('执行了getattr')return super().__getattr__(item)a = A()
a.class_name  # 没有任何输出
a.name        # 执行了getattribute 并报错

(2)魔法方法进阶

class DjangoRequest:  # django的requestdef method(self):  # django特有的方法methodprint(f'{self}--method')class DrfRequest(object):  # drf的requestdef __init__(self, req):self._request = req  # 组合将传入的对象作为一个属性self.data = dict()  # drf_request特有的属性dictdef __getattr__(self, attr):  # 重写了魔法方法__getattr__try:return getattr(self._request, attr)  # 从定义的属性中找这个属性里面的属性except AttributeError:return self.__getattribute__(attr)  # 触发异常没有这个属性django_req = DjangoRequest()
drf_req = DrfRequest(django_req)# 问题一:
print(drf_req.data)
# 问题二:
drf_req._request.method()
# 问题三
drf_req.method()
# 问题四:
drf_req.xxx
  • 问题一:print(drf_req.data)的执行流程
    • 非常的简单,直接在drf_req中找到实例属性data,直接输出即可
  • 问题二:drf_req._request.method()的执行流程
    • 首先找到drf_req中找到实例属性_request,这个很简单直接找到了
    • 由于这个属性是DjangoRequest()得到的实例django_req,所以可以继续从这个实例中找属性
    • 继续找method这个属性,在django_req实例中,并且这个属性是个方法,所以可以加括号直接执行
    • 最终输出<__main__.DjangoRequest object at 0x00000203E4B67FD0>--method
  • 问题三drf_req.method()的执行流程
    • 首先找到drf_req中找到实例属性method,这次并没有找到,所以将触发魔法方法__getattr__
    • 首先执行getattr反射方法,从self._request这个实例中找attr属性,即从django_req实例中找method属性
    • 能够找到,所以直接执行method方法
  • 问题四drf_req.xxx的执行流程
    • 首先找到drf_req中找到实例属性xxx,这次任没有找到,所以将触发魔法方法__getattr__
    • 首先执行getattr反射方法,从self._request这个实例中找attr属性,即从django_req实例中找xxx属性
    • 并没有找到这个xxx属性,且反射方法第三个参数为空,即没有找到的情况下,将会报错AttributeError
    • 通过异常捕获执行object__getattribute__方法,抛出了异常

【4】request源码重点部分分析

(1)前言:

  • 这里不对data进行分析,主要是分析如何保留源来的request方法

  • 在前面已经对APIView的源码进行分析过了,所以这里也仅对这request进行分析

(2)重要代码分析

image-20240413134606437

  • 现在代码执行到了APIView的dispatch方法

    • 在执行initialize_request之前的request是普通的request
    • 但是在这步之后的request就是强大的request了,它将完美继承普通request的所有属性方法,还有自己的一套属性方法
  • 执行了initialize_request

    • 将request作为一个参数传入了Request类中,当然这个里还有其他的参数,但是不是重点,不做过多赘述
  • 来到Request中

    • 如果看懂了魔法进阶中的内容方法,看这里的话,将毫无压力
    • 首先将传入的request参数作为属性,放在了_request这个属性中
    • 然后可以看到__getattr__魔法方法
      • 当调用封装以后的request的时候,首先会在自己的属性中找需要的内容
      • 当找不到的时候,就会来到这里先执行反射方法
      • 在_request中进行反射,查找这个属性,所以通过包装以后的request方法时,是可以完美继承以前request的所有属性方法的
      • 当找不到的时候,又因为反射方法getattr没有第三个参数,所以将会抛出异常,被捕获以后又执行object的方法进行捕获,最终抛出异常
  • 还可以看到Request类中

    • 是没有老的request的method、POST、get等属性的,所以从新版的request中确实找到的是老版本的属性
    • 还可以看到一个伪装成属性的data
      • 这个data就是新版,它将所有所有传入的数据,无论是什么格式,无论是什么请求,都将数据放在这个data中,并且是一个普通的字典,方便开发者使用
    • 还可以看到一个伪装成属性的query_params
      • query_params 属性通常用于访问请求的查询参数
      • 在DRF的视图中,可以使用它来提取URL中的查询参数
        • 例如 ?param1=value1&param2=value2 中的 param1param2

(3)小结

  • request之前的所有属性和方法,是被完美继承的,老的东西任然可以使用
    • request._request 就是老的request
  • request.data:将请求体中的数据(无论是什么方法,还是什么格式的数据)都将放在一个字典中
  • request.query_params:将访问请求的查询参数分装在这个属性中,方便提取URL参数,贴合restful规范

【5】补充requets.META

(1)介绍

  • 首先通过观察,发现在Request中是没有这个属性的

    • 即这个是老版本的request的属性
  • 在Django框架中,每个传入的HTTP请求都会由一个HttpRequest对象表示。

  • 这个对象包含了很多关于当前请求的信息,其中META属性是一个Python字典,包含了所有可用的HTTP头部信息。META字典中的键都是字符串,但它们的值可能是字符串或列表(如果HTTP头部有多个值)。

(2)常用属性

  1. PATH_INFO:一个字符串,表示请求的路径。例如,对于URL http://www.example.com/myapp/PATH_INFO的值将是/myapp/
  2. REMOTE_ADDR:客户端的IP地址。注意,如果使用了代理或负载均衡器,这个值可能不是最终用户的真实IP。
  3. HTTP_HOST:请求的Host头部信息,即客户端发送请求时指定的域名和端口(如果有的话)。
  4. HTTP_USER_AGENT:用户代理字符串,通常包含了关于客户端浏览器或其他发送请求的软件的信息。
  5. HTTP_ACCEPT:客户端能够处理的内容类型列表。
  6. HTTP_ACCEPT_LANGUAGE:客户端的首选语言。
  7. HTTP_ACCEPT_ENCODING:客户端接受的内容编码。
  8. HTTP_REFERER:在之前的web页面中用户点击链接来访问当前页面的地址(如果存在的话)。
  9. HTTP_X_FORWARDED_FOR:如果使用了HTTP代理或负载均衡器,这个头部可能包含客户端的原始IP地址。但请注意,这个头部可以被伪造,因此不能完全信任它。
  10. CSRF_TOKEN:Django的CSRF保护机制使用的令牌。在表单提交时,这个令牌会被包含在请求中,以验证请求是否来自受信任的来源。
  • 请注意,META字典中的键都是大写,并且以HTTP_开头(除了上面提到的几个特殊键)。
  • 这是因为HTTP头部是大小写不敏感的,但在Python字典中键是大小写敏感的。所以,为了保持一致性,Django将所有HTTP头部的键转换为大写,并在前面加上HTTP_前缀。
    • 例如,HTTP头部的Content-TypeMETA字典中将以HTTP_CONTENT_TYPE的形式出现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/824728.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Web】DASCTF X GFCTF 2022十月挑战赛题解

目录 EasyPOP hade_waibo EasyLove BlogSystem EasyPOP 先读hint.php sorry.__destruct -> secret_code::secret() exp: $anew sorry(); $bnew secret_code(); $a->password"suibian"; $a->name"jay"; echo serialize($a); 真暗号啊&…

web项目中jsp页面不识别el表达式

如果使用el表达式出现下图问题 ** 解决办法 ** 这是因为maven创建项目时&#xff0c;web.xml头部声明默认是2.3&#xff0c;这个默认jsp关闭el表达式 修改web.xml文件开头的web-app的版本 <?xml version"1.0" encoding"UTF-8"?> <web-app x…

Python爬取猫眼电影票房 + 数据可视化

目录 主角查看与分析 爬取可视化分析猫眼电影上座率前10分析猫眼电影票房场均人次前10分析猫眼电影票票房占比分析 主角查看与分析 爬取 对猫眼电影票房进行爬取&#xff0c;首先我们打开猫眼 接着我们想要进行数据抓包&#xff0c;就要看网站的具体内容&#xff0c;通过按F12…

Postman之安装

Postman工具之介绍与安装 Postman是什么&#xff1f;Postman有几种安装方式&#xff1f; Postman是什么&#xff1f; postman是一款http客户端的模拟器&#xff0c;它可以模拟发出各种各样的网络请求&#xff0c;用于接口测试。 Postman有几种安装方式&#xff1f; 两种&…

4.17 网络编程

思维导图 select实现TCP并发服务器 #include <myhead.h> #define SER_IP "192.168.125.26" #define SER_PORT 8888int main(int argc, const char *argv[]) {int sfd socket(AF_INET,SOCK_STREAM,0);if(sfd -1){perror("socket error");return -1…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt 小区物业管理系统 的设计与实现

一.项目介绍 系统分为管理员 和 业主 两块&#xff1a; 管理员点击进入到系统操作界面&#xff0c;可以对首页、业主信息管理、管理员信息管理、 楼栋和房屋信息管理、物业费管理、地下停车位管理、公告信息管理、报修信息管理、 投诉管理以及个人信息等功能模块 …

libftdi1学习笔记 5 - SPI Nor Flash

目录 1. 初始化 2. CS控制例子 3. 读ID 3.1 制造商 3.2 容量大小 3.3 设置IO类型 3.3.1 setQSPIWinbond 3.3.2 setQSPIMxic 3.3.3 setQSPIMicrochip 3.3.4 setQSPIMicron 4. 写保护 5. 等待空闲 6. 擦除扇区 7. 页编程 8. 页读 9. 写 10. 读 11. 验证 基于M…

cesium加载高层级离线影像地图瓦片(天地图、19级Arcgis)

实际加载效果如图&#xff1a; 1、下载离线地图瓦片方式&#xff08;多种任选其一&#xff0c;个人倾向于Qgis工具&#xff09;&#xff1a; 方式1、采用第三方下载工具如&#xff1a;91卫图、水经注、全能电子地图下载器、bigemap等等。&#xff08;这些有的下载层级不够&…

Spring Boot:Web应用开发之登录与退出的实现

Spring Boot 前言实现登录功能配置拦截器 实现退出功能 前言 登录与退出功能作为 Web 应用中的基础且重要的组成部分&#xff0c;直接关系到用户的安全和隐私保护。通过实现登录与退出功能&#xff0c;可以对用户的身份进行验证和授权&#xff0c;确保只有合法的用户才能访问特…

Qwen1.5大语言模型微调实践

在人工智能领域&#xff0c;大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;的兴起和广泛应用&#xff0c;为自然语言处理&#xff08;NLP&#xff09;带来了前所未有的变革。Qwen1.5大语言模型作为其中的佼佼者&#xff0c;不仅拥有强大的语言生成和理…

vue3【详解】选项式 API 实现逻辑复用

抽离逻辑代码到一个函数函数命名约定为 useXxxx格式 ( React Hooks 也是 )在 setup 中引用 useXxx 函数 演示代码&#xff1a;实时获取鼠标的坐标 逻辑封装 useMousePosition.js // 导入 ref, onMounted, onUnmounted import { ref, onMounted, onUnmounted } from "vue…

锐捷云桌面的安装

按下 <DEL> 键进入 BIOS setup 界面&#xff08;初始密码为 admin &#xff09;。 输入密码之后就进入 BIOS 的 Main 界面 设置服务器 BMC IP 地址。 a 云服务器启动后&#xff0c;在 BIOS 的主页面&#xff0c;把光标移到 [Server Mgmt] 项。 b 选择 [BMC Network C…

游戏前摇后摇Q闪E闪QE闪QA等操作

备注&#xff1a;未经博主允许禁止转载 个人笔记&#xff08;整理不易&#xff0c;有帮助&#xff0c;收藏点赞评论&#xff0c;爱你们&#xff01;&#xff01;&#xff01;你的支持是我写作的动力&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_w…

基于Springboot的社区防疫物资申报系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区防疫物资申报系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

Quartz + SpringBoot 实现分布式定时任务

文章目录 前言一、分布式定时任务解决方案二、Quartz是什么&#xff1f;1.quartz简介2.quartz的优缺点 二、Quartz分布式部署总结 前言 因为应用升级&#xff0c;由之前的单节点微服务应用升级为集群微服务应用&#xff0c;所以之前的定时任务Spring Scheduled不再适用了&…

SV-704LW 无线WIFI网络音柱

SV-704LW 无线WIFI网络音柱(工业级) 一、描述 SV-704LW是深圳锐科达电子有限公司的一款壁挂式WIFI无线网络音柱&#xff0c;通过WIFI无线接入到WIFI覆盖的网络中&#xff0c;可将网络音源通过自带的功放和喇叭输出播放&#xff0c;其采用防水设计&#xff0c;功率可以从30W到6…

如何利用FLUENT计算流体力学方法解决大气与环境领域流动问题

ANSYS FLUENT是目前全球领先的商用CFD 软件&#xff0c;市场占有率达70%左右&#xff0c;是工程师和研究者不可多得的有力工具。由于采用了多种求解方法和多重网格加速收敛技术&#xff0c;因而FLUENT能达到最佳的收敛速度和求解精度。灵活的非结构化网格和基于解的自适应网格技…

从OWASP API Security TOP 10谈API安全

1.前言 应用程序编程接口&#xff08;API&#xff09;是当今应用驱动世界创新的一个基本元素。从银行、零售、运输到物联网、 自动驾驶汽车、智慧城市&#xff0c;API 是现代移动、SaaS 和 web 应用程序的重要组成部分&#xff0c;可以在面向客 户、面向合作伙伴和内部的应用程…

计算机组成原理 — 控制单元的功能

控制单元的功能 控制单元的功能微操作命令分析取指周期间址周期执行周期中断周期 控制单元的功能控制单元的外特性输入信号输出信号 控制信号举例不采用CPU内部总线的方式取指周期间址周期执行周期 采用CPU内部总线的方式取指周期间址周期执行周期 多级时序系统机器周期时钟周期…

反激电源——TL431及光耦反馈电路计算(不涉及环路补偿)

一、TL431及光耦反馈电路 TL431以及光耦电路是反激的副边反馈类型电路中的常见应用。 其反馈工作原理为&#xff1a;当副边的输出电压升高时&#xff0c;TL431的REF点采样电压也会升高&#xff0c;使得TL431的导通量增加&#xff0c;同时光耦内部的发光二极管流过的电流也增大&…