【Python】简单Web框架从零开始(二):Http请求格式

简介: 一、前言 上一篇文章已经搭建了一个简单的http的网络服务器,可以接收浏览器的请求并简单响应。这期开始介绍http协议的具体格式以及如何解析,通过理解http的协议格式,就可以正确识别出客户端的请求内容,并根据对应的处理方法做出正确的响应。

【Python】简单Web框架从零开始(二):Http请求格式


目录


【Python】简单Web框架从零开始(一):Web服务


一、前言


   上一篇文章已经搭建了一个简单的http的网络服务器,可以接收浏览器的请求并简单响应。这期开始介绍http协议的具体格式以及如何解析,通过理解http的协议格式,就可以正确识别出客户端的请求内容,并根据对应的处理方法做出正确的响应。


二、HTTP请求格式介绍


   为了可以快速掌握,可以启动第一期的服务程序,然后通过浏览器访问对应的地址(http://127.0.0.1,服务器会在接收到客户端数据后将其打印出来,从而方便的进行结构分析。


微信截图_20220424002746.png

图1


   图1展示了浏览器访问http://127.0.0.1/地址的请求数据内容,具体内容解释如下:


       1. 第一行为请求行包含:请求方法(本图为GET),访问服务器的地址路径(本图为根路径/),HTTP的版本(本图为1.1),这3个数据之间使用空格分隔开。


       2. 中间为实体头信息包含通用头信息、请求头信息、实体头信息。其中部分是客户端的相关信息及功能支持情况,还有一些特殊的例如CookieCache相关的信息。


       3. 最后会有一个空行(\r\n)表示实体头内容的结束标记。


       4. 请求数据体,本图中由于没有任何额外的数据,因此没有后续内容输出。


微信截图_20220424002755.png

图2


   图2展示了浏览器带参数的请求,请求地址为http://127.0.0.1/test?username=abc&password=123456。其他内容都是和图1差不多一样的,注意后面的空行是存在的。这种请求的方式参数都显示在url地址中,如果参数够多整个url就非常的长长长


微信截图_20220424002815.png

图3


   图3展示了浏览器POST带参数请求,请求地址为http://127.0.0.1/test,参数数据存放在实体头的后面即请求数据体,参数的数据格式与图3放在地址中的内容是一样的,这样就可以让url看起来简单,并且“隐藏”参数信息。注意这里实体头里面有一个Content-Length属性数据表示后面HTTP数据体的长度,Content-Type属性表示HTTP数据体的格式类型。


微信截图_20220424002829.png

图4


   图4是另一种格式的POST请求,Content-Typemultipart/form-data,并且额外带有boundary的属性值。HTTP数据体的内容格式稍微复杂了一些,采用[--boundary]进行分割,并对每个参数可以额外指定类型,包括支持上传文件。


微信截图_20220424002837.png

图5


   图5是访问静态文件的请求格式,与图1基本一致,只是地址中包含了要请求下载的文件名和文件格式,服务器只需要按照正确的格式将需要的静态文件内容发送给客户端就可以完成下载。


   下图是完整的HTTP请求格式说明:


微信截图_20220424002849.png


三、解析HTTP请求


   快速理解客户端请求的格式后,就可以进行请求数据解析。每条连接发送请求数据后,服务器读取数据并开始进行解析,然后调用对应的处理方法后返回正确的响应数据就可以完成交互通讯。


   1. 从首行内容解析开始,利用了将socket的recv方式改为文件的read方式,可以简化处理过程,如下代码:


# 使用文件的方式操作socket
rfile = self.sock.makefile() #mode="r" 读取 "w"写入
# 解析第一行
first_line = rfile.readline()
# GET/POST/HEAD url&args HTTP/1.1
method_location_version = first_line.split()


   2. 实体头内容格式是固定的,每一行格式为:[name]:[空格][数据][\r\n] 表示属性name的对应数据值,部分数据值需要继续拆分。实体头内容完后会跟着一个独立的空行[\r\n]。可以以这个作为实体头结束的标记


while True:
    line = fd.readline().strip()
    if not line :
         break
    name_value = line.split(": ", 1)
    if not name_value or len(name_value) != 2:
        break # error format
    name = name_value[0]
    value = name_value[1]
    self.request_headers[name] = value.strip()


   3. 最后就是解析参数了,类似uesrname=abc&password=123456这样的格式可以使用urlparse.parse_qs进行解析成一个字典{"username"=["abc"],"password"=["123456"]},但multipart/form-data格式的解析就需要自己实现,我在自带库没有找到相关的解析方法,如果有知道的希望可以告诉我一下


# url中存放参数 /xxxx?a=v1&b=v2...
index = self.request_route.find("?")
if index > 0 and len(self.request_route) > index:
    arguments = urlparse.parse_qs(self.request_route[index+1:])
    self.request_arguments.update(arguments)
# 解析请求内容的格式
form_str_type = self.request_headers.get("Content-Type", None)
if not form_str_type:
    return  # 如果没有指定类型则不理会了
# 将参数放在正文中,但格式和放在url里面的一样
if form_str_type.find("application/x-www-form-urlencoded") >= 0:
    arguments = urlparse.parse_qs(self.raw_body_data)
    self.request_arguments.update(arguments)
# 将参数放在正文中,采用多附件表单格式
elif form_str_type.find("multipart/form-data") >= 0:
    boundary_index = form_str_type.rfind("boundary=")
    if boundary_index < 0:
        print("Invalid multipart/form-data: no boundary")
        return
    arguments = self.parse_multipart_formdata(form_str_type[boundary_index+len("boundary="):])
    self.request_arguments.update(arguments)


四、后记


   截至目前介绍了http服务的创建和客户端请求格式的解析,接下来会继续介绍http响应的格式以及如何处理客户端的请求,从而真正完成网站的基本交互逻辑。另外我实现的这个简单web框架仅支持HTTP/1.0和HTTP/1.1的请求,最新的HTTP/2还没接触过。


欢迎微信搜索"游戏测试开发"关注一起沟通交流。

相关文章
|
9月前
|
JSON 监控 API
掌握使用 requests 库发送各种 HTTP 请求和处理 API 响应
本课程全面讲解了使用 Python 的 requests 库进行 API 请求与响应处理,内容涵盖环境搭建、GET 与 POST 请求、参数传递、错误处理、请求头设置及实战项目开发。通过实例教学,学员可掌握基础到高级技巧,并完成天气查询应用等实际项目,适合初学者快速上手网络编程与 API 调用。
911 130
|
8月前
|
Java 数据处理 索引
(Pandas)Python做数据处理必选框架之一!(二):附带案例分析;刨析DataFrame结构和其属性;学会访问具体元素;判断元素是否存在;元素求和、求标准值、方差、去重、删除、排序...
DataFrame结构 每一列都属于Series类型,不同列之间数据类型可以不一样,但同一列的值类型必须一致。 DataFrame拥有一个总的 idx记录列,该列记录了每一行的索引 在DataFrame中,若列之间的元素个数不匹配,且使用Series填充时,在DataFrame里空值会显示为NaN;当列之间元素个数不匹配,并且不使用Series填充,会报错。在指定了index 属性显示情况下,会按照index的位置进行排序,默认是 [0,1,2,3,...] 从0索引开始正序排序行。
625 0
|
8月前
|
存储 Java 数据处理
(numpy)Python做数据处理必备框架!(一):认识numpy;从概念层面开始学习ndarray数组:形状、数组转置、数值范围、矩阵...
Numpy是什么? numpy是Python中科学计算的基础包。 它是一个Python库,提供多维数组对象、各种派生对象(例如掩码数组和矩阵)以及用于对数组进行快速操作的各种方法,包括数学、逻辑、形状操作、排序、选择、I/0 、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等等。 Numpy能做什么? numpy的部分功能如下: ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组 用于对整组数据进行快速运算的标准数学函数(无需编写循环)。 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。 线性代数、随机数生成以及傅里叶变换功能。 用于集成由C、C++
690 1
|
8月前
|
Java 数据挖掘 数据处理
(Pandas)Python做数据处理必选框架之一!(一):介绍Pandas中的两个数据结构;刨析Series:如何访问数据;数据去重、取众数、总和、标准差、方差、平均值等;判断缺失值、获取索引...
Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。 Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够轻松地从各种数据源中导入数据,并对数据进行高效的操作和分析。 Pandas 主要引入了两种新的数据结构:Series 和 DataFrame。
724 0
|
8月前
|
Java 数据处理 索引
(numpy)Python做数据处理必备框架!(二):ndarray切片的使用与运算;常见的ndarray函数:平方根、正余弦、自然对数、指数、幂等运算;统计函数:方差、均值、极差;比较函数...
ndarray切片 索引从0开始 索引/切片类型 描述/用法 基本索引 通过整数索引直接访问元素。 行/列切片 使用冒号:切片语法选择行或列的子集 连续切片 从起始索引到结束索引按步长切片 使用slice函数 通过slice(start,stop,strp)定义切片规则 布尔索引 通过布尔条件筛选满足条件的元素。支持逻辑运算符 &、|。
405 0
|
8月前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
9月前
|
机器学习/深度学习 算法 PyTorch
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
261 1
|
10月前
HTTP协议中请求方式GET 与 POST 什么区别 ?
GET和POST的主要区别在于参数传递方式、安全性和应用场景。GET通过URL传递参数,长度受限且安全性较低,适合获取数据;而POST通过请求体传递参数,安全性更高,适合提交数据。
882 2
|
9月前
|
数据采集 Web App开发 前端开发
处理动态Token:Python爬虫应对AJAX授权请求的策略
处理动态Token:Python爬虫应对AJAX授权请求的策略
|
9月前
|
机器学习/深度学习 算法 PyTorch
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
408 0

推荐镜像

更多