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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次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还没接触过。


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

相关文章
|
2月前
|
存储 监控 安全
如何在Python Web开发中确保应用的安全性?
如何在Python Web开发中确保应用的安全性?
|
2月前
|
安全 测试技术 网络安全
如何在Python Web开发中进行安全测试?
如何在Python Web开发中进行安全测试?
|
2月前
|
安全 关系型数据库 测试技术
学习Python Web开发的安全测试需要具备哪些知识?
学习Python Web开发的安全测试需要具备哪些知识?
37 4
|
2月前
|
开发框架 搜索推荐 数据可视化
Django框架适合开发哪种类型的Web应用程序?
Django 框架凭借其强大的功能、稳定性和可扩展性,几乎可以适应各种类型的 Web 应用程序开发需求。无论是简单的网站还是复杂的企业级系统,Django 都能提供可靠的支持,帮助开发者快速构建高质量的应用。同时,其活跃的社区和丰富的资源也为开发者在项目实施过程中提供了有力的保障。
|
2月前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
43 2
|
2月前
|
开发者 Docker Python
从零开始:使用Docker容器化你的Python Web应用
从零开始:使用Docker容器化你的Python Web应用
44 1
|
2月前
|
JSON 前端开发 API
使用Python和Flask构建简易Web API
使用Python和Flask构建简易Web API
115 3
|
2月前
|
监控 安全 测试技术
如何在实际项目中应用Python Web开发的安全测试知识?
如何在实际项目中应用Python Web开发的安全测试知识?
34 4
|
2月前
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
117 1
|
8月前
|
开发框架 开发者 Python
深入探究Python Web开发框架:Flask与Django
Python作为一种广泛应用于Web开发的编程语言,其拥有众多优秀的Web开发框架。本文将深入探讨其中两大知名框架——Flask与Django。通过对它们的概念与实践进行比较分析,帮助读者更好地理解和选择适合自己项目需求的Web开发框架。