flask_萧井陌视频学习(三)

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>


猜你喜欢

转载自blog.csdn.net/KevinGuo000/article/details/80909857