Flask
文章导读:本文主要是利用socket来编写一个简单的服务器,通过对浏览器地址栏中输入的地址信息来调用相应的函数,并将用户信息【用户名, 密码】存储到txt文件中,在在登入界面提交表单时能对用户信息进行验证。
功能:用户注册, 用户登入, 网页信息显示, 用户信息存储。
[这个程序还存在一些小问题]
目录结构:
utils.py文件:
import time
def log(*args, **kwargs):
"""
对print函数进行封装,并输出时间。
:param args:
:param kwargs:
:return:
"""
format = '%b %d %Y %H:%M:%S >'
# time.time()返回unix time
value = time.localtime(int(time.time()))
# time.strftime()把时间变为str格式输出
dt = time.strftime(format, value)
print(dt, args, kwargs)
Server.py文件:
import socket
from utils import log
from request_class import Request
from routes import route_static
from routes import route_dict
def parsed_path(path):
"""
分离出path and query
:param path:
:return:
"""
# /search
# /search?kd=python&name=Kevin
"""
{
kd : python
name : Kevin
}
"""
index = path.find('?')
if index == -1:
return path, {}
else:
path, query_string = path.split('?', 1)
# kd=python&name=Kevin
args = query_string.split('&')
query = {}
for arg in args:
k, v = arg.split('=')
query[k] = v
return path, query
def error(code=404):
"""
错误页面
:param code:
:return e:
"""
e = {
404: 'GET HTTP/1.1 404 NOT FOUND\r\n\r\n<h1>Not Found</h1>',
}
return e.get(code, b'')
def response_for_path(path):
"""
调用path相对应的函数
:param path:
:return:
"""
path, query = parsed_path(path)
request.path = path
request.query = query
log('path: {}, query: {}'.format(request.path, request.query))
r = {
'/static': route_static,
}
r.update(route_dict)
response = r.get(path, error)
return response(request)
request = Request()
def run(host='', port=3000):
"""
启动服务器
:param host:
:param port:
:return:
"""
log('start at {}, {}'.format(host, port))
# 使用with防止程序崩溃
with socket.socket() as s:
# 绑定端口
s.bind((host, port))
while True:
# 监听
s.listen(5)
connection, address = s.accept()
# 为了简单,暂时这里只接受1024字节
r = connection.recv(1000)
r = r.decode('utf-8')
log('ip: {}\n request: {}'.format(address, r))
# 因为 chrome 会发送空请求导致 split 得到空 list
# 所以这里判断一下防止程序崩溃
if len(r.split()) < 2:
continue
# GET / HTTP/1.1\r\n ....
path = r.split()[1]
request.method = r.split()[0]
request.body = r.split('\r\n\r\n', 1)[1]
# 用response_for_path函数来调用path对应的函数
response = response_for_path(path)
# 发送数据
connection.sendall(response)
# 关闭连接
connection.close()
def main():
"""
主函数
:return:
"""
# 生成配置文件并运行
config = dict(
host='',
port=3000,
)
run(**config)
if __name__ == '__main__':
main()
routes.py文件:
from user import User
from message import Message
from utils import log
message_list = []
def template(name):
"""
读取template文件夹中的文件
:param name:
:return f:
"""
# 这里的文件路径为我电脑上的路径
path = '/home/kevin/programe/python/flask_xiao/day3/templates/' + name
with open(path, 'r', encoding='utf-8') as f:
return f.read()
def route_index(request):
"""
主页
:param request:
:return:
"""
header = 'HTTP/1.1 200 OK\r\nContent-Type=text/html\r\n'
body = template('index.html')
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
def route_login(request):
"""
登入函数
:param request:
:return r:
"""
header = 'HTTP/1.1 200 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')
def route_register(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_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')
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 = '<h1>消息版</h1>'
body = template('html_basic.html')
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')
def route_static(request):
"""
静态资料处理函数,处理图片
:param request:
:return img:
"""
filename = request.query.get('file', 'doge.gif')
#这里的文件路径为我电脑上的路径
path = '/home/kevin/programe/python/flask_xiao/day3/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
route_dict = {
'/': route_index,
'/login': route_login,
'/register': route_register,
'/message': route_message,
}
request_class.py文件:
import urllib.parse
class Request(object):
"""
定义一个Request类用来保存请求的数据
"""
def __init__(self):
# 默认为GET请求
self.method = 'GET'
self.path = ''
self.query = {}
self.body = ''
def form(self):
# username=Kevin&password=123
# 将body拆分开来
# 变为username=Kevin password=123
#这里有个小bug没有解决
body = self.body.split('&')
# 将其他格式转变为网页可以认识的格式,如空格转换为%20
args = urllib.parse.unquote(body)
f = {}
for arg in args:
k, v = arg.split('=')
f[k] = v
return f
db文件夹:
__init__.py文件:
from file_save_load import save, load
from utils import log
class Model(object):
"""
存储数据的基类
"""
#类方法的调用是 类名.方法
@classmethod
def db_path(cls):
"""
因为classmethod有个参数为class所以可以调用名字
:return path:
"""
class_name = cls.__name__
path = '{}.txt'.format(class_name)
return path
@classmethod
def all(cls):
"""
得到一个类所有存储的实例
:return:
"""
path = cls.db_path()
models = load(path)
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]
path = self.db_path()
save(l, path)
def __repr__(self):
class_name = self.__class__.__name__
properties = ['{}: ({})'.format(k, v) for k, v in self.__dict__.items()]
s = '\n'.join(properties)
return '< {}\n{} >\n'.format(class_name, s)
file_save_load.py文件:
import json
from utils import log
def save(data, path):
"""
存储数据的函数
data表示dict or list
path 表示存储路径
:param data:
:param path:
:return:
"""
# json 是一个序列化/反序列化list/dict 的库
# indent表示缩进
# ensure_ascii表示保存中文字符
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 or list
:param path:
:return:
"""
with open(path, 'r', encoding='utf-8') as f:
s = f.read()
log('Load: ', s)
return json.load(s)
user.py文件:
from __init__ import Model
class User(Model):
"""
用户类继承Model
"""
def new(self, form):
"""
获取用户名与密码
:param form:
:return:
"""
self.username = form.get('username', '')
self.password = form.get('password', '')
def validate_login(self):
"""
用户登入
:return username, password:
"""
return self.username == 'Kevin' and self.password == '123'
def validate_register(self):
"""
用户注册
:return:
"""
return len(self.username) > 2 and len(self.password) > 2
message.py文件:
from __init__ import Model
# 定义一个 class 用于保存 message
class Message(Model):
def new(self, form):
self.author = form.get('author', '')
self.message = form.get('message', '')
templates文件夹:
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 rows cols 都是属性, 用处上课讲 -->
<textarea name="message" rows="8" cols="40"></textarea>
<textarea name="author" rows="8" cols="40"></textarea>
<!-- button type=submit 才可以提交表单 -->
<button type="submit">POST 提交</button>
</form>
<form action="/messages" method="get">
<textarea name="message" rows="8" cols="40"></textarea>
<button type="submit">GET 提交</button>
</form>
{{messages}}
</body>
</html>
index.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<h1>Hello Kevin</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>
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>
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>