web框架推导
目录
软件开发架构
cs架构
bs架构
web框架的本质
所有的Web应用本质上就是一个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()
web服务本质就是在上述代码的基础上扩展出来的
在网页上直接输入ip+port(127.0.0.1:8080)得到的结果是127.0.0.1 发送的响应无效。原因是写的服务端向客户端发送的数据格式不符合HTTP协议。
想让客户端(浏览器)收到服务端发送的数据,需要遵循HTTP协议,因此在给客户端回复消息的时候必须按照HTTP协议规则加上响应状态行
低配版自定义web框架 --- 遵循HTTP协议
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框架的本质,还可以继续进行完善哦
中配版自定义web框架 --- 根据不同的url返回不同的内容
低配版 --- if判断
原理:将浏览器发送到服务端的数据进行处理,然后做一个判断即可
import socket
'''
b'GET /index HTTP/1.1\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'
'''
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()
中配版 --- 函数版
低配版的问题:虽然解决了不同url路径返回不同内容的需求,但是如果有很多路径要判断怎么办?难道挨个写if判断?
解决办法 --- 使用函数与列表结合
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
def index(url):
s = '这是{}'.format(url)
return bytes(s, encoding='utf-8')
def login(url):
s = '这是{}'.format(url)
return bytes(s, 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()
中配版 --- 返回HTML文档
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
def index(url):
s = '这是{}'.format(url)
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()
静动态网页
-
静态网页----网页能够显示,但是页面上的数据是写死的,万年不变
-
动态网页----数据实时获取,比如后端事件展示到html文件上,数据从数据库获取展示到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()
wsgiref模块
自定义web框架的不足之处
- 代码重复,服务端代码所有人都要重新写
- 手动处理http格式的数据 并且只能拿到url后缀 其他数据获取繁琐(数据格式一样处理的代码其实也大致一样 重复写)
- 并发的问题
借助wsgiref模块
基础版服务端 --- 根据url返回对应的数据
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() # 启动服务端
函数版服务端
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 服务端文件
- server.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
模板语法之Jinja2模块
原理
模板的原理就是字符串替换,只要在HTML页面中遵循jinja2的语法规则书写,内部就会按照指定的语法进行替换
模版语法案例一
- 将字典传递给html文件,并且可以在文件上方便快捷的操作字典数据
(注意要将该函数与路由对应关系加入到urls.py中)
from jinja2 import Template
def get_dict(env):
user_dic = {'username':'jason','age':18,'hobby':'read'}
with open(r'templates/04 get_dict.html','r',encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user=user_dic)
# 给get_dict.html传递了一个值 页面上通过变量名user就能够拿到user_dict,变量名是随意的,不过还是需要见名知意
return res
# get_dic.html
<body>
{{xxx}} // 获取字典
// 获取字典中的值的三种方式
{{xxx.get('username')}}
{{xxx['password']}}
{{xxx.hobbies}}
</body>
模板语法案例二 --- 后端数据库中数据展示到前端页面
def get_mysql(env):
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='1026',
database='user',
charset='utf8',
autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = 'select * from info;'
rows = cursor.execute(sql)
data_list = cursor.fetchall()
with open(r'template/get_mysql.html','r',encoding='utf-8') as f:
data = f.read()
temp = Template(data)
res = temp.render(data_list=data_list)
return res
# get_mysql.html
待定
自定义简易版本web框架请求流程
wsgiref模块
1.请求来的时候解析http格式的数据 封装成大字典
2.响应走的时候给数据打包成符合http格式 再返回给浏览器