详解Python web框架到底是怎么来的?

 前言

咱都知道软件开发的架构有两种,分别是C/S架构与B/S架构,本质上都是借助socket实现网络通信,因此Django作为一个web框架本质上也是一个socket服务端,浏览器则是客户端,我们可以自己实现简易的web框架来更好的理解Django。

Web框架推导

在介绍socket时,我们可以自定义一个socket服务端,这个socket服务端是最最原始的web框架,web服务的本质是基于下述代码扩展出来的。

import socket


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

while True:
    conn, addr = server.accept()
    data = conn.recv(1024)
    print(data)
    conn.send(b'web frame')
    conn.close()

但是使用上述代码开启服务端,在浏览器上访问127.0.0.1:80返回的结果是发送的相应无效,原因就是上述socket服务端向客户端发送的数据格式不符合HTTP协议的要求,如果想让客户端浏览器收到服务端发送的数据,就要遵循HTTP协议,因此服务端在向客户端发送数据的时候必须按照HTTP协议的规则加上相应状态行。

# 符合http协议的socket服务端
import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

while True:
    conn, addr = server.accept()
    data = conn.recv(1024)
    print(data)
    
    # 响应状态行
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    conn.send(b'web frame')
    conn.close()

上述代码就是web框架的本质,虽然简陋但是可以对上述代码进行装饰。在使用浏览器上网的时候,访问不同的网址就会返回不同的页面或者是内容,根据已有的代码就可以通过if判断实现客户端访问不同的url返回不同的数据或者页面。因此如何获取用户访问的url是什么就是需要解决的问题了,可以将浏览器向服务端发送请求时的数据打印出来进行分析,比如浏览器请求的url如下127.0.0.1:8080/index得到的data数据如下所示,发现第一行开始就可以看到/index,因此可以使用代码获取客户端访问的url:

'''
b'GET /index HTTP/1.1\r\n   # \r\n表示换行
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n
Sec-Fetch-Site: cross-site\r\n
Sec-Fetch-Mode: navigate\r\n
Sec-Fetch-User: ?1\r\n
Sec-Fetch-Dest: document\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n\r\n'
'''
# if判断实现根据不同的url返回不同的数据或页面
import socket


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data_list = data.decode('utf-8').split(' ')  # 将数据以空格切分
    print(data_list)
    current_path = data_list[1]  # 列表中的第二个元素就是路由
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    if current_path == '/index':
        conn.send(b'index')
    elif current_path == '/login':
        conn.send(b'login')
    else:
        conn.send(b'web frame')
    conn.close()

使用if判断虽然解决了访问不同url返回不同数据的需求,但是如果有非常多的路径要怎么办嘞?难道一个一个的挨个判断吗?显然不现实,所以可以采用函数与列表结合实现更简洁的实现不同url返回不同数据的需求。

import socket


def index(url):
    s = 'this is {}'.format(url)
    return bytes(s, encoding='utf-8')


def login(url):
    s = 'this is {}'.format(url)
    return bytes(s, encoding='utf-8')

# 定义一个url与函数功能的对应关系的列表
url_list = [
    ('/index', index),
    ('/login', login)
]

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

while True:
    conn, addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data_list = data.decode('utf-8').split(' ')
    print(data_list)
    current_path = data_list[1]
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')

    # 定义一个变量名用以保存函数的内存地址
    func = None
    for i in url_list:
        if i[0] == current_path:
            func = i[1]
            break
    if func:
        res = func(current_path)
    else:
        res = b'404 not found'
    conn.send(res)
    conn.close()

访问不同的url时,服务端不仅可以返回不同的数据也可以返回HTML页面:

import socket
import datetime

server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

def index(url):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    with open('time.time.html','r',encoding='utf-8') as f:

        data = f.read()
        
    # 在网页上定义好特殊符号,用字符串方法替换
    s = data.replace('sfsd',current_time)
    return bytes(s, encoding='utf-8')

def login(url):
    with open('myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
    return bytes(res,encoding='utf-8')

# 定义一个url与函数功能的对应关系的列表
url_list = [
    # (路由,视图函数)
    ('/index',index),
    ('/login',login)
]
while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data_list = data.decode('utf-8').split(' ')
    print(data_list)
    current_path = data_list[1]
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')

    # 定义一个变量名用以保存函数的内存地址
    func = None
    for i in url_list:
        if i[0] == current_path:
            func = i[1]
            break
    if func:
        res = func(current_path)
    else:
        res = b'404 not found'
    conn.send(res)
    conn.close()

通过上述的web框架推导过程已经可以实现客户端浏览器访问不同的url服务端能够返回不同的数据或者页面了,但是,重点来了,但是代码重复,每个开发web服务端的人都需要重新洗这段代码,并且是手动处理的http格式的数据,并且还只能拿到url后缀,还有就是并发的问题,说这么多并不是说上述代码推导过程没用,上述代码推导只是让小伙伴们更加理解web框架的本质,而提到的问题可以借助其他模块解决即wsgiref模块

wsgiref模块

借助wsgiref模块对http格式的数据进行处理时会方便很多,首先看一下该模块的基本用法:

from wsgiref.simple_server import make_server


def run(env,response):
    '''

    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,return [b'']
    '''
    # 响应首行 响应头
    response('200 OK', [])

    # env就是字典格式的HTTP数据
    print(env)
    current_path = env.get('PATH_INFO')  # 获取客户端浏览器请求的路由
    # wsgiref模块帮你处理号HTTP格式数据,封装成成字典
    if current_path == '/index':
        return [b'index']
    elif current_path == '/login':
        return [b'welcome']
    else:
        return [b'404 error']


if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)
    # 实时监听127.0.0.1:8080地址,只要有客户端来了都会交给run函数处理,即触发run函数的运行
    server.serve_forever()  # 启动服务端

借助wsgiref模块对http数据的处理,可以将web服务端的代码进行修改:

from wsgiref.simple_server import make_server
import datetime

# 将env将参数传给视图函数后,视图函数就可以根据浏览器发送给服务端的数据完成各种事件
def index(env):
    with open(r'template/myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
        return res

def longin(env):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    with open(r'template/time.time.html','r',encoding='utf-8') as f:
        res = f.read()
    res = res.replace('sfsd',current_time)
    return res

urls = [
    ('/index',index),
    ('/loging',longin)
]


def run(env,response):
    '''

    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,return [b'']
    '''
    # 响应首行 响应头
    response('200 OK', [])

    # env就是字典格式的HTTP数据
    print(env)
    current_path = env.get('PATH_INFO')
    # wsgiref模块帮你处理号HTTP格式数据,封装成成字典
    func = None
    for i in url_list:
        if current_path == i[0]:
            func = i[1]
            break
    if func:
        res = func(env)
    else:
        res = '404 not found'
    return [res.encode()]

if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)
    # 实时监听127.0.0.1:8080地址,只要有客户端来了都会交给run函数处理,即触发run函数的运行
    server.serve_forever()  # 启动服务端

看来看去上述代码还可以再改改,代码全部放在一个py文件中是不合理的,后期功能如果增多的话代码的管理或者功能的扩展都会变得困难,因此需要分文件,可以按照下述方式进行分文件,不同的文件中放不同的代码:

urls.py				路由与视图函数对应关系
views.py			视图函数的逻辑(后端业务逻辑)
templates文件夹	  专门存储html文件
sever.py			服务端文件

具体代码如下:

# sever.py
from wsgiref.simple_server import make_server
from urls import  urls
from views import *



def run(env,response):
    '''

    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,return [b'']
    '''
    # 响应首行 响应头
    response('200 OK', [])

    # env就是字典格式的HTTP数据
    print(env)
    current_path = env.get('PATH_INFO')
    # wsgiref模块帮你处理号HTTP格式数据,封装成成字典
    func = None
    for i in urls:
        if current_path == i[0]:
            func = i[1]
            break
    if func:
        res = func(env)
    else:
        res = '404 not found'
    return [res.encode()]

if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)
    # 实时监听127.0.0.1:8080地址,只要有客户端来了都会交给run函数处理,即触发run函数的运行
    server.serve_forever()  # 启动服务端
    
    
# urls.py
import views

urls = [
    ('/index',views.index),
    ('/loging',views.longin)
]


# views.py
import datetime

def index(env):
    with open(r'template/myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
        return res

def longin(env):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    with open(r'template/time.time.html','r',encoding='utf-8') as f:
        res = f.read()
    res = res.replace('sfsd',current_time)
    return res

总结

上文对web服务端是如何出现的进行了简单的推导,在实际开发中无需使用上述代码进行开发,使用现有的web框架比如django或者flask就可以完成,这两种框架都是基于socket服务端并且符合http协议的web框架,介绍上述推导过程希望可以帮助自己对web请求的理解。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你! 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux sudo指令提权

sudo指令 sudo 是在linux中用于以超级用户(root)权限执行命令的命令。它允许普通用户在执行特定命令时提升其权限,以完成需要超级用户权限的任务。sudo 的名称是 "superuser do" 的缩写。 格式 接受权限的用户登陆的主机 &#xff…

【MySQL】——关系数据库标准语言SQL(大纲)

🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL&#xff1a…

OceanBase集群扩缩容

​ OceanBase 数据库采用 Shared-Nothing 架构,各个节点之间完全对等,每个节点都有自己的 SQL 引擎、存储引擎、事务引擎,天然支持多租户,租户间资源、数据隔离,集群运行的最小资源单元是Unit,每个租户在每…

MCM备赛笔记——图论模型

Key Concept 图论是数学的一个分支,专注于研究图的性质和图之间的关系。在图论中,图是由顶点(或节点)以及连接这些顶点的边(或弧)组成的。图论的模型广泛应用于计算机科学、通信网络、社会网络、生物信息学…

如何在Docker下部署MinIO存储服务通过Buckets实现文件的远程上传

📑前言 本文主要是Linux下通过Docker部署MinIO存储服务实现远程上传的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风讲故事 &#…

Modern C++ 一个例子学习条件变量

目录 问题程序 施魔法让BUG浮出水面 条件变量注意事项 修改程序 问题程序 今天无意中看到一篇帖子,关于条件变量的,不过仔细看看发现它并达不到原本的目的。 程序如下,读者可以先想想他的本意,以及有没有问题: #…

洛谷P5732 【深基5.习7】杨辉三角(C语言)

入门递推题&#xff0c;就算你不是OIer也该知道的杨辉三角 同时这也是组合数的公式&#xff0c;很重要&#xff0c;因为常规组合数公式是阶乘运算会爆&#xff0c;而这个就不怎么会了 赋 arr[i][j]初值1&#xff0c;接下来就可以递推了 #include<stdio.h> int main() …

温度采样【通道选通】S9KEAZ128的PTA2和PTA3引脚无法拉高

1、问题记录&#xff1a;由18串温度采样修改成32串温度采样&#xff0c;增加一路adc采样&#xff0c;通过cd4051控制通道选通&#xff0c;代码中增加了相应的代码&#xff0c;发现增加的最后8路温度不能够控制&#xff0c;以24串为例&#xff0c;给温度传感器增加温度&#xff…

01-开始Rust之旅

1. 下载Rust 官方推荐使用 rustup 下载 Rust&#xff0c;这是一个管理 Rust 版本和相关工具的命令行工具。下载时需要连接互联网。 这边提供了离线安装版本。本人学习的机器环境为&#xff1a; ubuntu x86_64&#xff0c;因此选用第②个工具链&#xff1b; 1. rust-1.75.0-x86_…

CPU数据按行和按列读取性能差异浅析

改了一行代码&#xff0c;数组遍历耗时从10.3秒降到了0.5秒&#xff01; 两个简单的测试程序 定义一个同样大小的二维数组&#xff0c;然后循环遍历&#xff0c;对数组元素赋值。 array1.c 对数组按行进行访问 • array2.c 对数组按列进行访问、 编译运行&#xff0c;并用ti…

[小程序]页面的构建

一、视图容器组件 ①View 视图区域组件&#xff0c;类似于HTML中的div&#xff0c;可以用来按块实现页面布局效果&#xff0c;具体实例如下&#xff1a; <view class"dock"><view>A</view><view>B</view><view>C</view> &…

《Linux高性能服务器编程》笔记01

Linux高性能服务器编程 本文是读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 文章目录 Linux高性能服务器编程第05章 Linux网络编程基础API5.1 socket…

JOSEF约瑟 零序过流继电器LGL-110/AC AC220V 0.01~9.99A 柜内安装

LGY 、LGL零序过电压继电器 系列型号 LGY-110零序过电压继电器&#xff1b; LGL-110零序过电压继电器&#xff1b; LGL-110/AC零序过电压继电器&#xff1b; LGL-110静态零序过电流继电器 &#xff11; 应用 LGL-110 型零序过电流继电器用作线路和电力设备的零序过电流保护。…

【Arduino】无法上传程序到开发板,报错 avrdude: ser_open(): can‘t set com-state for “\\.\COM6“

问题描述 在尝试将项目上传到Arduino板子时&#xff0c;尽管开发板已被正确连接&#xff0c;并且IDE中能够正常读取到开发板信息&#xff0c;但是上传过程中仍然出现了问题。 下面是IDE中显示的开发板信息&#xff1a; 当尝试上传程序时&#xff0c;控制台报错信息如下&#…

【迅搜19】扩展(二)TNTSearch和JiebaPHP方案

扩展&#xff08;二&#xff09;TNTSearch和JiebaPHP方案 搜索引擎系列的最后一篇了。既然是最后一篇&#xff0c;那么我们也轻松一点&#xff0c;直接来看一套非常有意思的纯 PHP 实现的搜索引擎及分词方案吧。这一套方案由两个组件组成&#xff0c;一个叫 TNTSearch &#xf…

【大数据Hive】hive 行列转换使用详解

目录 一、前言 二、使用场景介绍 2.1 使用场景1 2.2 使用场景2 三、多行转多列 3.1 case when 函数 语法一 语法二 操作演示 3.2 多行转多列操作演示 四、多行转单列 4.1 concat函数 语法 4.2 concat_ws函数 语法 4.3 collect_list函数 语法 4.4 collect_set函…

获取域控的方法

在域渗透中、作为渗透测试人员&#xff0c;获取域控的权限基本上可以获取整个内网的权限 1.高权限读取本地密码 当域管理员在域成员机器上登录进行工作的时候&#xff0c;会将明文密码保存在本地进行的lsass.exe&#xff0c;可以通过 mimikatz来读取到本地的明文密码。 priv…

MySQL基础笔记(9)事务

一.简介 所谓事务&#xff0c;是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即&#xff0c;这些操作要么同时成功&#xff0c;或者同时失败——OS中有原语不可分割的概念&…

蓝桥杯、编程考级、NOC、全国青少年信息素养大赛—scratch列表考点

1、小小情报员&#xff08;202309scratch四级24题&#xff09; 1.准备工作 &#xff08;1&#xff09;选择背景 Colorful City&#xff1b; &#xff08;2&#xff09;保留角色小猫&#xff0c;选择角色Ballerina。 2.功能实现 &#xff08;1&#xff09;角色小猫初始位置…

灵活扩展:深入理解MyBatis插件机制

第1章&#xff1a;MyBatis插件的重要性 大家好&#xff0c;我是小黑&#xff0c;咱们今天要聊的是MyBatis插件&#xff0c;MyBatis&#xff0c;大家都不陌生&#xff0c;它是一个ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;让咱们在操作数据库时能更加优雅。但今…