实现登录
虽然 app.py 运行方式从之前的 app.run 改成了 socketio.run,但是不影响 Flask 传统的开发模式,如渲染模板、处理 cookie 和 session 等。socketio.run 在保留了 Flask 传统功能以外,还增加了 WebSocket 支持。因此,登录功能我们依然可以使用 HTTP 协议的方式。在 app.py 文件中添加以下代码。
class ResultCode:
OK = 200
ERROR_PARAMS = 400
ERROR_SERVER = 500
def result(code=ResultCode.OK, data=None, message=""):
return {"code": code, "data": data or {}, "message": message}
@app.route("/login", methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template("login.html")
else:
username = request.form.get('username')
if not username:
return result(ResultCode.ERROR_PARAMS, message="请输入用户名")
elif UserManager.has_user(username):
return result(ResultCode.ERROR_PARAMS, message="此用户名已存在")
session['username'] = username
return result()
上述代码中,我们实现了登录 URL 与视图。如果用 GET 请求,则返回登录模板;如果用 POST 请求,则执行登录操作。这里我们简化了登录过程,用户只要输入用户名即可。如果条件都符合,则把用户名存储在 session 中。为了让代码更加规范,我们添加了返回状态码的 ResultCode 类,以及返回 JSON 格式的 result 函数。并且我们用了 UserManager 类来管理当前登录的用户,UserManager 类的代码如下。


上述代码中,定义了两个类属性,分别为 __instance
和 _users
,其中 __instance
是用于保存单例对象的,_users
用来保存所有用户信息。通过重写 __new__
方法使得 UserManager 仅能被创建一次,也就是单例对象。接着定义了一系列的类方法,分别为添加用户(add_user)、移除用户(remove_user)、获取用户(get_user)、判断用户是否存在(has_user)、获取当前用户(get_current_user)以及获取所有用户的用户名(all_username)。这些类方法可使后续处理即时消息变得更加简单。
此时在浏览器中访问 http://127.0.0.1:5000/login ,可以看到如图 10-1 所示的效果。
在后续的请求中,为了保证用户必须在登录后才能进行访问,我们实现一个 login_required 装饰器,代码如下。
from functools import wraps
from flask import session, redirect
def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not session.get("username"):
return redirect("/login")
else:
return func(*args, **kwargs)
return wrapper

上述装饰器代码中,我们判断 session 中是否包含 username,如果没有,则重定向到登录页面,否则就正常执行被装饰的函数。