纯手撸web框架
- socket代码需要我们自己写;
- http格式的数据自己处理(只能拿到用户输入的路由);
# web框架可以理解成服务器,也就是B/S架构中的服务端
import socket
server = socket.socket()
# 绑定的参数是元组:IP地址、端口号
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn, addr = server.accept()
# 浏览器客户端请求二进制数据,请求数据长度为1024个字节
data = conn.recv(1024)
# 将获取的二进制数据解码成字符串
data = data.decode("utf-8")
# print(data,type(data))
# 返回请求的状态信息
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 获取路由根目录进行判断
current_path = data.split(' ')[1]
if current_path == '/index':
conn.send(b'welcome to index Page!')
elif current_path == '/login':
conn.send(b'welcome to login page!')
else:
conn.send(b'<h2>Hello World</h2>')
conn.close()
=======================================================================================
data的详细数据信息
========================================================================================
GET /login HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
基于wsgiref模块的web服务器的实现
- 帮助我们封装了socket代码;
- 大字典封装处理了http格式的数据;
web服务网关接口
- 请求来时,帮助我们自动拆分http格式的数据并封装成非常方便处理的数据格式。
- 响应走时,帮我们将数据再打包成符合http格式。
from wsgiref.simple_server import make_server
# 业务逻辑的视图函数,封装成views.py
def index(env):
return 'welcome to index page.'
def login(env):
return 'welcome to login page.'
def error(env):
return '404 Not Found!'
# 路由层:urls.py
urls = [
('/index',index),
('/login',login),
]
def run(env,response):
'''
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的所有数据
'''
# 由wxgiref模块帮你处理好http格式的数据,封装成了大字典
print(env)
response('200 OK',[])
# 获取路由根目录
current_path = env.get('PATH_INFO')
func = None
for url in urls:
if current_path == url[0]:
func = url[1]
print(url[0])
# 匹配到一个之后,立刻结束for循环
break
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
# 将字符串转换为二进制数据,返回到路由映射的前端页面
return [res.encode('utf-8')]
if __name__ == '__main__':
"""
实时监听127.0.0.1:8080端口地址,如果有客户端发起请求
则会交给源函数处理,即触发run函数运行
"""
server = make_server('127.0.0.1',8080,run)
server.serve_forever()
根据上述的编写逻辑,将代码解耦封装如下:
start_server.py
from wsgiref.simple_server import make_server
from app.urls import urls
from app.views import *
def run(env,response):
'''
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的所有数据
'''
# 由wxgiref模块帮你处理好http格式的数据,封装成了大字典
print(env)
response('200 OK',[])
# 获取路由根目录
current_path = env.get('PATH_INFO')
func = None
for url in urls:
if current_path == url[0]:
func = url[1]
print(url[0])
# 匹配到一个之后,立刻结束for循环
break
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
# 将字符串转换为二进制数据
return [res.encode('utf-8')]
if __name__ == '__main__':
"""
实时监听127.0.0.1:8080端口地址,如果有客户端发起请求
则会交给源函数处理,即触发run函数运行
"""
server = make_server('127.0.0.1',8080,run)
server.serve_forever()
urls.py
from app import views
# 路由层:urls.py
urls = [
('/index',views.index),
('/login',views.login),
]
views.py
# 业务逻辑的视图函数,封装成views.py
def index(env):
return 'welcome to index page.'
def login(env):
return 'welcome to login page.'
def error(env):
return '404 Not Found!'
- urls.py:路由与视图函数的映射关系;
- views.py:视图函数(后端业务的处理逻辑);
- templates文件夹:专门用来存储html文件;
按照功能的不同划分之后,后续添加功能只需要在urls.py书写对应关系后,在views.py书写业务逻辑即可。
动静态网页
- 静态网页:
- 页面上的数据是写死的
- 动态网页:
- 数据是实时获取的,由视图函数将处理的结果反馈到前端的html页面中
- 功能需求:
- 后端获取当前时间展示到html页面上;
- 数据字典是从数据库中获取的展示到html页面上;
- views.py
import datetime
from jinja2 import Template
# 业务逻辑的视图函数,封装成views.py
def get_time(env):
current_time = datetime.datetime.now()
# 如何将后端获取的数据“传递”给前端的html文件?
# 方式一:读写文件
with open(r'templates/currentTime.html','r',encoding='utf-8') as f:
data = f.read()
# 字符串处理函数渲染前端页面
current_time = str(current_time)
data.replace("current_time",current_time)
print(data)
# 在后端将html页面处理好之后再返回给前端
return data
def get_dict(env):
# 定义一个存储用户信息的字典
user_dic = {'username':'trainingl','age':21,'hobby':'studying'}
user_list = [
{'username':'zhangsan','age':21,'hobby':'studying'},
{'username':'lisi','age':22,'hobby':'reading'},
{'username':'wangwu','age':20,'hobby':'sleeping'},
{'username':'zhangliu','age':23,'hobby':'playing'}
]
with open(r'templates/get_dict.html','r',encoding='utf-8') as f:
data = f.read()
# 模版语法是在后端起作用的
tmp = Template(data)
res = tmp.render(user=user_dic,users=user_list)
# 给get_dict.html传递了一个值 页面上通过变量名userInfo就能够拿到user_dict
return res
- urls.py
from app import views
# 路由层:urls.py
urls = [
('/index',views.index),
('/login',views.login),
('/get_time',views.get_time),
('/get_dict',views.get_dict)
]
- templates/get_dict.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>字典传值</title>
</head>
<body>
<h2>模版语法之Jinja2模块</h2>
{{user}}<br/>
取值的三种方式:<br/>
{{user.get('username')}}<br/>
{{user.age}}<br/>
{{user['hobby']}}<br/><hr/>
<table border="1px red solid" cellspacing="0" align="center">
<caption>用户信息表</caption>
<tr>
<th>用户名</th>
<th>年龄</th>
<th>爱好</th>
</tr>
{% for user_item in users%}
<tr>
<td>{{ user_item.username}}</td>
<td>{{ user_item.age}}</td>
<td>{{ user_item.hobby}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
小结
自定义简易版本的web框架请求流程(基于wsgiref模块):
- 请求来时,解析http格式的数据,封装成大字典
env
或request
; - 响应走时,给数据打包成符合http格式,再返回给浏览器