与前端联调注册/登录接口(part 1)
前方高能
咱们今天讲的内容,可能和前端关联比较大,如果没有经验的同学,或者说只想专注后端的同学,你们直接copy前端的代码即可。
秉着毁人不倦的思想,笔者打算细讲一部分前端的内容,毕竟还有一部分读者是前后兼修的。
如果你们也想React相关知识,在我这里可能不能系统地学到,但是在 测试平台(序) 中也介绍了相关的前端学习建议和地址,希望大家恶补一下,毕竟这方面也不算难。
编写登录接口
与注册接口类似,甚至更加简单,先理一下思路:
用户通过页面(这里暂时还没有)输入用户名和密码,点击登录按钮进行登录,如果成功的话则将用户信息放入返回即可。如果用户名或者密码不正确,则给出提示,拒绝让用户登录!
Talk is cheap, show me the code!
from datetime import datetime from sqlalchemy import or_ from app.middleware.Jwt import UserToken from app.models import db from app.models.user import User from app.utils.logger import Log class UserDao(object): log = Log("UserDao") @staticmethod def register_user(username, name, password, email): """ :param username: 用户名 :param name: 姓名 :param password: 密码 :param email: 邮箱 :return: """ try: users = User.query.filter(or_(User.username == username, User.email == email)).all() if users: raise Exception("用户名或邮箱已存在") # 注册的时候给密码加盐 pwd = UserToken.add_salt(password) user = User(username, name, pwd, email) db.session.add(user) db.session.commit() except Exception as e: UserDao.log.error(f"用户注册失败: {str(e)}") return str(e) return None @staticmethod def login(username, password): try: pwd = UserToken.add_salt(password) # 查询用户名/密码匹配且没有被删除的用户 user = User.query.filter_by(username=username, password=pwd, deleted_at=None).first() if user is None: return None, "用户名或密码错误" # 更新用户的最后登录时间 user.last_login_at = datetime.now() db.session.commit() return user, None except Exception as e: UserDao.log.error(f"用户{username}登录失败: {str(e)}") return None, str(e)
简单解释一下,我们这边新建了一个login的方法,接受参数是username和password,接着我们通过orm筛选出第一条username与password匹配且没有被删除的用户。
注意: 如果这里没有这个用户的话,user变量会是None,所以我采用了判断None的方式
最后我们把该用户的最后登录时间改成了当前时间。然后提交到了orm的session,这句话等同于执行sql。
思考
我们在上一个接口里面,注册完用户以后哈,是没有返回User实例信息的,但是这个接口不同,用户登录以后,需要拿到用户的信息以及token给前端处理。
但是我们的用户实例并不是一个JSON对象,所以我们要为它做一些处理。
接着我们编写pity/handler/factory.py,目的是为了转换orm对象为dict。
from datetime import datetime class ResponseFactory(object): @staticmethod def model_to_dict(obj, *ignore: str): data = dict() for c in obj.__table__.columns: if c.name in ignore: # 如果字段忽略, 则不进行转换 continue val = getattr(obj, c.name) if isinstance(val, datetime): data[c.name] = val.strftime("%Y-%m-%d %H:%M:%S") else: data[c.name] = val return data @staticmethod def model_to_list(data: list, *ignore: str): return [ResponseFactory.model_to_dict(x, *ignore) for x in data]
解释一下2个方法,第一个是将User此类实例转换为dict。由于orm实例都继承了db.Model类,所以他们都有table属性,也就有了相关字段的信息。
ignore参数是一个可变字符串参数,当我们遇到需要忽略掉的字段时,可以派上用场。
这里可以看到笔者针对datetime类对象做了特殊处理,这是因为JSON中没有对应的datetime格式,所以我们需要对他也进行转换。
剩下的字段,直接通过set key value的方式赋值给data字典即可。
model_to_list也比较简单,属于一个列表生成式,对每条orm实例都进行了转换,最终返回一个list(orm object)
编写route方法
最后我们就需要编写路由方法了,在pity/controller/auth/user.py新增如下方法:
@auth.route("/login", methods=['POST']) def login(): data = request.get_json() username, password = data.get("username"), data.get("password") if not username or not password: return jsonify(dict(code=101, msg="用户名或密码不能为空")) user, err = UserDao.login(username, password) if err is not None: return jsonify(dict(code=110, msg=err)) user = ResponseFactory.model_to_dict(user, "password") token = UserToken.get_token(user) if err is not None: return jsonify(dict(code=110, msg=err)) return jsonify(dict(code=0, msg="登录成功", data=dict(token=token, user=user)))
这边还是先判断了username和password是否为空的情况,其次调用了UserDao的login方法,返回user和err信息。
这里还有一个细节就是,我把用户的password给隐藏了
user = ResponseFactory.model_to_dict(user, "password")`
接着通过UserToken.get_token成功将对象转换成token,拿到token后返回了这样的对象:
{ "code": 0, "data": { "user": {}, "token": "" } }
验证
依旧是熟悉的postman出场时间,简单地测试一下:
- 密码错误的情形
image
- 密码正确的返回
image
可以看到,最后登录时间也正常显示了,token也生成了,password也没有暴露。想起抖音上的一些文案:
婚期已定,父母满意,对我体贴.....
有一种未来可期的感觉。
求饶
今天好像干货比较多,看来没有足够的时间留给前端联调了,会在下一节补充起来吧,至于名字就也不改了吧。