[success] 尝试搭建框架
我们所要做的工程目录划分
1.一个db 文件夹用来存储数据
2.一个models 文件夹用来操作数据
3.一个static文件夹用来保存静态文件
4.一个templates 用来保存html
5.一个routes.py 用来做路由映射
6.一个server.py 用来做启动文件
7.一个utils.py 用来配置log
[info] 编写记录log -- utils.py
import time
def log(*args, **kwargs):
# time.time() 返回 unix time
format = '%Y/%m/%d %H:%M:%S'
value = time.localtime(int(time.time()))
dt = time.strftime(format, value)
print(dt, *args, **kwargs)
[info] 编写服务器入口文件--server.py
1.Request 类进行,保存不同路由映射函数信息
2.response_for_path 处理run中获取的地址进行路由映射
3.run 方法用来启动server服务器
[danger] Request 保存路由映射对应请求信息类
1.初始化参数 method 记录每一个映射路由的请求
2.path 记录每一个路由的地址路径
3.body 保存每一个路由的body 内容主要针对post
4.query 记录每一个get请求链接上的参数 配合parsed_path 函数
5.form 用来处理post 请求参数urllib.parse .unquote(v) 处理v 是因为post 的时候空格是+
class Request:
def __init__(self):
self.method = "GET"
self.path = ""
self.body = ""
self.query = {}
def form(self):
args = self.body.split("&")
f = {}
for arg in args:
k, v = arg.split("=")
f[k] = urllib.parse .unquote(v)
return f
# 实例化对象
request = Reuqest()
[danger] parsed_path 处理get 请求
def parsed_path(path):
index = path.find("?")
if index == -1:
return path, {}
else:
path, query_string = path.split("?", 1)
args = query_string.split("&")
query = {}
for arg in args:
k, v = arg.split('=')
query[k] = v
return path, query
[danger] 编写一个异常处理的erro文件返回404
def error(request, code=404):
e = {
404: b'HTTP/1.1 404 NOT FOUND\r\n\r\n<h1>NOT FOUND</h1>',
}
return e.get(code, b'')
[danger] response_for_path 处理路由映射关系
1.parsed_path用来处理 get 请求方法,将get 请求方法清洗,返回地址和请求参数
2.return 返回的是将路由映射保存的request 对象传入到对应的映射方法
def response_for_path(path):
# 如果你是get
path, query = parsed_path(path)
request.path = path
request.query = query
r = {
"/static":route_static,
}
r.update(urls)
response = r.get(path, error)
return response(request)
[danger] run 启动服务器文件
def run(host='', port=3000):
# 在控制台打印端口,和服务器启动时间
log('start at', '{}:{}'.format(host, port))
# 使用 with 可以保证程序中断的时候正确关闭 socket 释放占用的端口
with socket.socket() as s:
s.bind((host, port))
while True:
s.listen(5)
connection, address = s.accept()
r = b""
buffer_size = 1024
while True:
r_connection = connection.recv(buffer_size)
r += r_connection
if len(r_connection) <= 1024:
break
r = r.decode('utf-8')
if len(r.split())<2:
continue
# 拆分原始数据 获取 请求方法和路径 GET / HTTP/1.1
path = r.split()[1]
request.method = r.split()[0]
# 利用"\r\n\r\n" 是将请求体分割格式,获取请求体
request.body = r.split("\r\n\r\n", 1)[1]
# 路由映射
response = response_for_path(path)
connection.sendall(response)
# 处理完请求, 关闭连接
connection.close()
[info] 路由映射文件 -- routes.py
1.这个文件主要处理每一个,映射函数的请求
2.核对templates 文件的搭配
3.以及处理一些简单的模板
[danger] 处理读取HTML函数的 -- template
1.注意一定要对open 中的encoding 进行编码,win默认是gbk模式
def template(name):
path = "template/" + name
with open(path, "r",encoding='utf-8') as f:
return f.read()
[danger] 配置url 参数映射字典
urls = {
'/': route_index,
'/login': route_login,
'/register': route_register,
'/messages': route_message,
}
[danger] 编写静态文件的映射函数 -- route_static
def route_static(request):
"""
两种情况的处理
path, query = response_for_path('/static?file=doge.gif')
path '/static'
"""
filename = request.query.get('file', 'doge.gif')
path = 'static/' + filename
with open(path, 'rb') as f:
header = b'HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\n'
img = header + b'\r\n'+ f.read()
return img
[danger] 编写第一个主页映射函数 -- route_index
def route_index(request):
"""
主页的处理函数, 返回主页的响应
"""
header = 'HTTP/1.1 210 VERY OK\r\nContent-Type: text/html\r\n'
body = template('index.html')
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
[danger] 编写登录逻辑处理的---route_login
1.传的参数中的request 保存的是每一个函数的,请求时候的处理信息
2.如果是post 请求处理创建的Request 类中form 方法保存的body中的内容
3.利用replace 替代我们在html 中特殊格式化的数据,这个页面最后渲染不应该属于任何请求,而是所有请求处理的展示内
容改变
def route_login(request):
header = 'HTTP/1.1 210 VERY OK\r\nContent-Type: text/html\r\n'
if request.method == 'POST':
form = request.form()
u = User.new(form)
if u.validate_login():
result = '登录成功'
else:
result = '用户名或者密码错误'
else:
result = ''
body = template('login.html')
body = body.replace('{{result}}', result)
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
[danger] 编写注册处理函数 --- route_register
def route_register(request):
header = 'HTTP/1.1 210 VERY OK\r\nContent-Type: text/html\r\n'
if request.method == 'POST':
# HTTP BODY 如下
# username=gw123&password=123
# 经过 request.form() 函数之后会变成一个字典
form = request.form()
u = User.new(form)
if u.validate_register():
u.save()
result = '注册成功<br> <pre>{}</pre>'.format(User.all())
else:
result = '用户名或者密码长度必须大于2'
else:
result = ''
body = template('register.html')
body = body.replace('{{result}}', result)
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
[danger] 模拟简单的psot get请求 ---route_message
# message_list 存储了所有的 message
message_list = []
def route_message(request):
log('本次请求的 method', request.method)
if request.method == 'POST':
form = request.form()
msg = Message.new(form)
log('post', form)
message_list.append(msg)
# 应该在这里保存 message_list
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = template('html_basic.html')
# '#'.join(['a', 'b', 'c']) 的结果是 'a#b#c'
msgs = '<br>'.join([str(m) for m in message_list])
body = body.replace('{{messages}}', msgs)
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
[info] 编写数据处理文件夹 model
1.我们处理数据库的连接模块进行拆分,成三个小py文件
2.__init__.py用来编写model 的父类和数据存放方法
3.user.py 用来处理用户信息判断验证
4.message.py 用来处理信息验证
[danger] 编写init文件
1.class Model 类用来处理文件的保存和读取
--- 注意这个类几点说明
--- db_path 中使用了cls.__name__ 的方法,可以获取使用类的类名,因为类名和对象不同点在于,类名是唯一的,做成
父类后所有子类就变成唯一的
--- new 相当于创建了对应的类
--- all 是获取了所有储存文件的内容
--- save 将所有内容保存到文件目录中
2.save 方法用来保存文件信息
3.load 用来加载文件信息
def save(data, path):
"""
本函数把一个 dict 或者 list 写入文件
data 是 dict 或者 list
path 是保存文件的路径
indent 是缩进
ensure_ascii=False 用于保存中文
"""
s = json.dumps(data, indent=2, ensure_ascii=False)
with open(path, 'w+', encoding='utf-8') as f:
log('save', path, s, data)
f.write(s)
def load(path):
"""
本函数从一个文件中载入数据并转化为 dict 或者 list
path 是保存文件的路径
"""
with open(path, 'r', encoding='utf-8') as f:
s = f.read()
log('load', s)
return json.loads(s)
# Model 是用于存储数据的基类
class Model(object):
@classmethod
def db_path(cls):
classname = cls.__name__
path = 'db/{}.txt'.format(classname)
return path
@classmethod
def new(cls, form):
# 下面一句相当于 User(form) 或者 Msg(form)
m = cls(form)
return m
@classmethod
def all(cls):
"""
得到一个类的所有存储的实例
"""
path = cls.db_path()
models = load(path)
log('log', models)
ms = [cls.new(m) for m in models]
return ms
def save(self):
"""
save 函数用于把一个 Model 的实例保存到文件中
"""
models = self.all()
log('models', models)
models.append(self)
# __dict__ 是包含了对象所有属性和值的字典
l = [m.__dict__ for m in models]
log("lmodel", l)
path = self.db_path()
save(l, path)
def __repr__(self):
"""
对象的显示形式,__dict__ 返回的是初始化 参数
"""
classname = self.__class__.__name__
properties = ['{}: ({})'.format(k, v) for k, v in self.__dict__.items()]
s = '\n'.join(properties)
return '< {}\n{} >\n'.format(classname, s)
[danger] 用来进行用户注册登录判断的 user.py
from models import Model
class User(Model):
def __init__(self, form):
self.username = form.get('username', '')
self.password = form.get('password', '')
def validate_login(self):
return self.username == 'gua' and self.password == '123'
def validate_register(self):
return len(self.username) > 2 and len(self.password) > 2
[danger] 用来进行简单的post,get message.py
from models import Model
# 定义一个 class 用于保存 message
class Message(Model):
def __init__(self, form):
self.author = form.get('author', '')
self.message = form.get('message', '')
[info] ## html 文件
[danger] ##### login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册登录页面</title>
</head>
<body>
<h1>登录</h1>
<form action="/login" method="post">
<input type="text" name="username" placeholder="请输入用户名">
<br>
<input type="text" name="password" placeholder="请输入密码">
<br>
<button type="submit">登录</button>
</form>
<h3>{{result}}</h3>
</body>
</html>
[danger] ##### register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<h1>注册</h1>
<form action="/register" method="post">
<input type="text" name="username" placeholder="请输入用户名">
<br>
<input type="text" name="password" placeholder="请输入密码">
<br>
<button type="submit">注册</button>
</form>
<h3>{{result}}</h3>
</body>
</html>
[danger] ##### index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>吃瓜主页</title>
</head>
<body>
<h1>吃瓜</h1>
<a href="/login">Login</a>
<img src="/static?file=doge.gif"/>
<img src="/static?file=doge1.jpg"/>
<img src="/static?file=doge2.gif"/>
</body>
</html>
[danger] ##### html_basic.html
<!DOCTYPE html>
<!-- 注释是这样的, 不会被显示出来 -->
<!--
html 格式是浏览器使用的标准网页格式
简而言之就是 标签套标签
-->
<!-- html 中是所有的内容 -->
<html>
<!-- head 中是放一些控制信息, 不会被显示 -->
<head>
<!-- meta charset 指定了页面编码, 否则中文会乱码 -->
<meta charset="utf-8">
<!-- title 是浏览器显示的页面标题 -->
<title>例子 1</title>
</head>
<!-- body 中是浏览器要显示的内容 -->
<body>
<!-- html 中的空格是会被转义的, 所以显示的和写的是不一样的 -->
<!-- 代码写了很多空格, 显示的时候就只有一个 -->
很 好普通版
<h1>很好 h1 版</h1>
<h2>很好 h2 版</h2>
<h3>很好 h3 版</h3>
<!-- form 是用来给服务器传递数据的 tag -->
<!-- action 属性是 path -->
<!-- method 属性是 HTTP方法 一般是 get 或者 post -->
<!-- get post 的区别上课会讲 -->
<form action="/messages" method="post">
<!-- textarea 是一个文本域 -->
<!-- name 属性, 用处上课讲 -->
<textarea name="message"></textarea>
<textarea name="author"></textarea>
<!-- button type=submit 才可以提交表单 -->
<button type="submit">POST 提交</button>
</form>
<form action="/messages" method="get">
<textarea name="message"></textarea>
<button type="submit">GET 提交</button>
</form>
{{messages}}
</body>
</html>
网友评论