1. web框架本质
web框架本质上可以看成一个功能强大的socket服务端,用户的浏览器可以看成拥有可视化界面的socket客户端
通过网络请求实现数据交互,可简单的将web框架看做是对前端、数据库的全方位整合
2. 手写简易版web框架
服务端
import socketserver = 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_serverdef 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_serverdef 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 = Nonefor 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 errordef run(request, response):""":param request:与请求相关数据:param response: 与响应相关数据:return: 返回给客户端的数据"""# print(request) # 字典类型的数据(HTTP请求相关)response("200 OK", []) # 固定格式ask_path = request.get("PATH_INFO")func_name = Nonefor 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 Templatedef 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/