47. web框架
1. web框架本质
web框架本质上可以看成一个功能强大的socket服务端,用户的浏览器可以看成拥有可视化界面的socket客户端
通过网络请求实现数据交互,可简单的将web框架看做是对前端、数据库的全方位整合
2. 手写简易版web框架
服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 9009))
server.listen(5)
while True:
sock, addr = server.accept()
data = sock.recv(1024)
data_str = data.decode('utf-8') # 将客户端请求数据转为字符串
print(data_str) # 打印客户端浏览器的请求数据
sock.send(b'HTTP1.1 200 OK\r\n\r\n') # 服务端响应的数据需要符合HTTP响应格式
ask_path = data_str.split(' ')[1] # 使用空字符串切割,索引为1的元素即浏览器请求的路径
if ask_path == '/login':
sock.send(b'login exists')
elif ask_path == '/register':
sock.send(b'register exists')
else:
sock.send(b'404 Not Found')
当端口之后不带路径时
同时打印的/favicon.ico可以忽略
当端口之后带有路径时
以上简易版web框架的缺点:
(1)服务端socket代码过于重复
(2)HTTP数据请求没有更多的处理方式(只能切割空字符)
(3)无法实现服务端高并发
3. 基于wsgiref模块搭建web框架
服务端
from wsgiref import simple_server
def run(request, response):
"""
:param request:与请求相关数据
:param response: 与响应相关数据
:return: 返回给客户端的数据
"""
# print(request) # 字典类型的数据(HTTP请求相关)
response("200 OK", []) # 固定格式
ask_path = request.get("PATH_INFO")
if ask_path == "/":
return [b'empty']
elif ask_path == "/login":
return [b'login exists']
else:
return [b'404 error']
if __name__ == '__main__':
server = simple_server.make_server('127.0.0.1', 9000, run) # 一直监听本机9000端口,一旦有请求访问,自动触发run函数的运行
server.serve_forever()
打印部分request请求字典类型的数据
不带路径时
带有路径时
4. wsgiref优化
4.1 优化方案思路
(1)问题
如果网站很多,就要添加很多个elif
每个分支下的功能都不相同,可能会有比较复杂的逻辑
(2)函数优化
将匹配和功能,封装成元组和函数
新的问题是,所有功能函数放在一个py文件中过于冗余,不便于后期管理维护
(3)模块优化
根据功能的不同拆分成不同的py文件
urls.py 对应关系的存储
views.py 业务逻辑的编写
想要新增功能,只需要在urls.py中添加对应关系,view.py中编写业务逻辑函数
新的问题是,业务函数代码中,可能会频繁的使用到不同的HTML页面
(4)模板优化
为了避免文件类型的混乱,单独创建一个文件夹存储所有的HTML文件
templates文件夹 存储项目所需的HTML文件
新的问题是,项目中的HTML文件,也有可能需要用到css、js、第三方框架文件
(5)静态优化
HTML所需要的css、js、第三方框文件都是很少改变的文件
所以可以统一放在某个文件夹下
static文件夹 存储项目所需的“静态文件”
4.2 优化方案实现
[1] 一个py文件
当有很多请求响应的情况下不可能无限制编写if判断语句,应该设置对应关系并动态调用.
from wsgiref import simple_server
def login():
return "login exists"
def register():
return "register exists"
def error():
return "error"
urls = (("/login", login), ("/register", register))
def run(request, response):
"""
:param request:与请求相关数据
:param response: 与响应相关数据
:return: 返回给客户端的数据
"""
# print(request) # 字典类型的数据(HTTP请求相关)
response("200 OK", []) # 固定格式
ask_path = request.get("PATH_INFO")
func_name = None
for url_one in urls:
if ask_path == url_one[0]: # 将请求的路径与已存在的路径进行比对
func_name = url_one[1] # 将路径名作为值赋值给函数名
break # 匹配成功则停止比对
if func_name: # for循环完毕之后,func_name有可能是None,因此要进行判断
res = func_name()
else:
res = error()
return [res.encode("utf8")]
if __name__ == '__main__':
server = simple_server.make_server('127.0.0.1', 9000, run) # 一直监听本机9000端口,一旦有请求访问,自动触发run函数的运行
server.serve_forever()
[2] 根据功能划分模块
(1)views.py 存储功能函数
def login():
return "login exists"
def register():
return "register exists"
def error():
with open(r"templates/error.html", "r", encoding="utf-8") as f:
return f.read()
(2)urls.py 存储对应关系
from views import *
urls = (("/login", login), ("/register", register))
(3)server.py 核心代码
from wsgiref import simple_server
from urls import urls
from views import error
def run(request, response):
"""
:param request:与请求相关数据
:param response: 与响应相关数据
:return: 返回给客户端的数据
"""
# print(request) # 字典类型的数据(HTTP请求相关)
response("200 OK", []) # 固定格式
ask_path = request.get("PATH_INFO")
func_name = None
for url_one in urls:
if ask_path == url_one[0]: # 将请求的路径与已存在的路径进行比对
func_name = url_one[1] # 将路径名作为值赋值给函数名
break # 匹配成功则停止比对
if func_name: # for循环完毕之后,func_name有可能是None,因此要进行判断
res = func_name()
else:
res = error()
return [res.encode("utf8")]
if __name__ == '__main__':
server = simple_server.make_server('127.0.0.1', 9001, run) # 一直监听本机9000端口,一旦有请求访问,自动触发run函数的运行
server.serve_forever()
(4)模板文件与静态文件
templates文件夹:存储HTML文件
static文件夹:存储HTML文件所需静态资源(暂时为空)
总结:拆分后如果想要新增一个功能,只需要在views.py中编写函数,urls.py中添加对应关系即可。
5. 动静态网页
5.1 概念
静态网页:数据和布局在编写 HTML 文件时就已经固定下来,不随用户的请求或时间的变化而改变。
动态网页:在服务器端运行脚本语言(如 PHP、Python、JavaScript 或 Java 等)来生成和处理数据,然后再将处理后的数据以 HTML 格式发送至浏览器。
eg:1.页面上展示当前时间(后端获取传递给前端页面)
2.页面上展示数据库数据(后端连接数据库查询数据再传递给页面)
5.2 案例1
需求:页面上展示当前时间在4.2[2]的基础上,定义一个获取时间的函数,并添加对应关系
views.py
def get_time():
c_time = time.strftime("%Y-%m-%d %X")
with open(r'templates/get_time.html', 'r', encoding='utf-8') as f:
data = f.read() # 读取出来的数据是整个HTML代码
new_data = data.replace('abcdefg', c_time) # 将整个HTML代码的指定字符串内容替换为时间数据
return new_data
urls.py
from views import *
urls = (("/login", login), ("/register", register), ("/get_time", get_time))
get_time.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>abcdefg</p>
</body>
</html>
5.3 案例2
需求:将字典传递到页面上,并且在页面上还可以使用字典的操作方法
在4.2[2]的基础上,定义一个获取字典的函数,并添加对应关系
views.py
def get_dict():
info = {'name': 'avril', 'pwd': 123, 'hobby': 'sing'}
with open(r'templates/get_dict.html', 'r', encoding='utf-8') as f:
data = f.read()
new_data = data.replace('abcdefg', str(info)) # 将字典转换成了字符串格式
return new_data
urls.py
from views import *
urls = (("/login", login), ("/register", register), ("/get_time", get_time), ("/get_dict", get_dict))
get_dict.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>abcdefg</p>
</body>
</html>
以上代码将字典以字符串的形式在页面上展示,但是无法使用字典的操作方法
为了解决这个问题,引入了jinja2模块
6. jinja2模块
安装jinja2模块:pip install jinja2
模块功能介绍:在编写前后端不分离项目的时候,使用该模块提供的模板语法简单快速的在HTML页面可以用类似于后端的代码语法操作数据
在4.2[2]与5.3的基础上,对get_dict的py文件与html文件稍作修改
views.py
from jinja2 import Template
def get_dict():
info = {'name': 'avril', 'pwd': 123, 'hobby': 'sing'}
with open(r'templates/get_dict.html', 'r', encoding='utf-8') as f:
data = f.read()
proj = Template(data)
res = proj.render({'user_dict': info}) # 给页面传递一个变量名是user_dict,值为info(对应值是字典)的数据
return res
get_dict.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>字典数据展示</p>
<p>{{user_dict}}</p>
<p>{{user_dict.name}}</p>
<p>{{user_dict['pwd']}}</p>
<p>{{user_dict.get('hobby')}}</p>
</body>
</html>
以上HTML代码使用python语法操作数据
7. 框架请求流程
7.1 流程图
7.2 关键字解释
[1] urls.py
urls = (("/login", login), ("/register", register), ("/get_time", get_time))
括号里路径专业名词称之为"路由"
括号里函数名专业名词称之为"视图函数"
urls.py专业名词称之为"路由层"
[2] views.py
def login():
return "login exists"
def register():
return "register exists"
该文件用来编写业务逻辑代码,可以是函数、也可以是类
函数称之为"视图函数",类则称之为"视图类"
views.py称之为"视图层"
[3] templates文件夹
用来存储HTML文件
HTML文件称之为"模板文件"
templates文件夹称之为"模板层"
[4] static文件夹
用来存储静态文件资源
页面所需的css文件、js文件、图片、第三方文件可统称为"静态资源"
8. python主流web框架
Django框架官网:https://www.djangoproject.com/
Flask框架官网:https://flask.palletsprojects.com/en/3.0.x/
Fastapi框架官网:https://fastapi.tiangolo.com/
Pyramind框架官网:https://trypyramid.com/
Tornado框架官网:https://www.tornadoweb.org/en/stable/
Sanic框架官网:https://github.com/sanic-org/sanic
Fastapi框架官网:https://fastapi.tiangolo.com/
Aiohttp框架官网:https://docs.aiohttp.org/en/stable/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现