手写一个简单的web框架
纯手撸简易版的web框架
import socket
# 括号后面什么都不加默认tcp协议
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) # 请求协议
"""
b'GET / 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
sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"\r\n
sec-ch-ua-mobile: ?0\r\n
sec-ch-ua-platform: "Windows"\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\r\n
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\r\n
Sec-Fetch-Site: none\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'
"""
# 把请求从二进制改为字符的形式,以便获取请求内容
data = data.decode('utf-8')
# tcp是流式协议,这样写就不用每发一个响应就写一个
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 以空格为切割符,把请求切成一个列表,这样用户访问的后缀就是索引为1的字符串。例如:http://127.0.0.1:8080/index,index就是用户访问的后缀
current_path = data.split(' ')[1]
# print(current_path) # '/index'
# 当用户的后缀为index时,返回一个HTML页面
if current_path == '/index':
with open(r'一个页面.html', 'rb') as f:
conn.send(f.read())
elif current_path == '/liu':
conn.send(b'liujieshishazi')
else:
conn.send(b'hello web')
# 关闭链接
conn.close()
手写框架的不足之处
1、代码重复
2、手动处理请求数据繁琐
3、并发问题
基于wsgiref模块进行接收数据和响应数据
导入wsgiref模块:
from wsgiref.simple_server import make_server
from wsgiref.simple_server import make_server
def run(env, response):
"""
:param env: 请求相关的所有数据,返回一个字典,wsgiref模块帮你处理好http格式的数据,封装成了字典让你更加方便的操作数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
"""
response('200 OK', []) # 响应首行 响应头
# 从字典中取出数据,key为PATH_INFO的value是用户传的后缀
current_path = env.get('PATH_INFO')
if current_path == '/index':
return [b'index']
elif current_path == '/liu':
return [b'liuhousheng']
return [b'404']
if __name__ == '__main__':
"""
实时监听127.0.0.1:8080地址,只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)
"""
server = make_server('127.0.0.1', 8080, run)
# 启动服务器
server.serve_forever()
wsgiref模块代码优化与封装
"""
运行文件 用wsgiref模块写出主干
urls.py 路由与视图函数对应关系
views.py 视图函数(后端业务逻辑)
templates文件夹 专门用来储存HTML文件
"""
# 按照功能的不同拆分之后,后续功能只需要在urls.py书写对应的关系后再去views.py书写业务逻辑即可
运行文件代码
from wsgiref.simple_server import make_server
# 导入文件urls
from urls import urls
# 导入views
from views import *
def run(env, response):
"""
:param env: 请求相关的所有数据,返回一个字典,wsgiref模块帮你处理好http格式的数据,封装成了字典让你更加方便的操作数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
"""
response('200 OK', []) # 响应首行 响应头
# 从字典中取出数据,key为PATH_INFO的value是用户传的后缀
current_path = env.get('PATH_INFO')
# 定义一个变量为空
func = None
# for循环urls列表得到一个个元组
for url in urls:
# 取出元组中索引为0的元素,判断是否有获取到的路由
if current_path == url[0]:
# 判断成功把元组中索引为1的元素赋值给func变量
func = url[1]
# 判断成功立刻结束for循环
break
# 判断func是否为空
if func:
# func不为空,加括号运行,并把env传过去,以便再进行取值操作
res = func(env)
else:
# func为空,运行404的函数
res = error(env)
# 把返回的值编码成二进制的形式传给浏览器
return [res.encode('utf-8')]
if __name__ == '__main__':
"""
实时监听127.0.0.1:8080地址,只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)
"""
server = make_server('127.0.0.1', 8080, run)
# 启动服务器
server.serve_forever()
urls文件代码
from views import *
urls = [
('/index', index),
('/login', login),
('/error', error)
]
views文件代码
def index(env):
with open(r'templates/index.html', 'r', encoding='utf-8') as f:
return f.read()
def login(env):
with open(r'templates/login.html', 'r', encoding='utf-8') as f:
return f.read()
def error(env):
with open(r'templates/error.html', 'r', encoding='utf-8') as f:
return f.read()
模板语法之jinja2模块
给前端传一个字典数据
# 写在views文件中
# 并且在urls文件中写入路由对应关系:('/get_dic', get_dic)
# 导入jinja2模块
from jinja2 import Template
def get_dic(env):
# 创建一个字典
user_dic = {'username': 'liujie', 'password': 123, 'age': 18, 'hobby': 'read'}
# 打开文件,读模式
with open(r'templates/get_dic.html', 'r', encoding='utf-8') as f:
data = f.read()
# 给get_dic.html传递字典user_dic,用变量名user接收,在前端页面就可以使用user访问字典内容
tmp = Template(data)
res = tmp.render(user=user_dic)
# 把HTML文件返回浏览器渲染
return res
HTML输出字典内容的几种方法
<body>
<h1>这是一个标签</h1>
// 固定语法,通过双大括号从后端取值
{{ user }}
// jinja2与Python语法接近
// 从字典通过key取值的三种方法
<div>用户姓名:{{ user.get('username') }}</div>
<div>用户密码:{{ user.password }}</div>
<div>用户年龄:{{ user['age'] }}</div>
</body>
结合mysql数据库给前端提交数据
# 写在views文件中
# 并且在urls文件中写入路由对应关系:('/get_user', get_user)
import pymysql
from jinja2 import Template
def get_user(env):
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='get_user', # 一定要指定库
charset='utf8', # 编码千万不要加-,会报错
# 是否自动提交
autocommit=True
) # 链接数据库
# 产生一个游标对象(就是用来帮你执行命令的)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# SQL语句
sql = 'select * from user'
# 返回值是你当前SQL语句所影响的行数
affect_rows = cursor.execute(sql)
# 拿到所有的数据
data_list = cursor.fetchall()
with open(r'templates/get_user.html', 'r', encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user_dic=data_list)
return res
模板语法的for循环
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>年龄</th>
<th>爱好</th>
</tr>
</thead>
<tbody>
{% for user in user_dic %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
<td>{{ user.hobby }}</td>
</tr>
{% endfor %}
</tbody>
标签:web,get,index,server,user,env,手写,Django,data
From: https://www.cnblogs.com/liuhousheng/p/16977502.html