29 后端功能接口实战(一):后端接口该如何开发?
你好,我是Barry。
在前面课程中,我们已经学过了后端接口开发的前置知识,并且我们也通过一些功能案例尝试了接口开发。
接下来的两节课,我们就结合直播视频平台的需求,来完成系统化的接口开发,让你掌握独立完成功能接口开发的能力。这节课我们先来梳理开发流程规范和接口需求,并通过创建passport认证模块编写登录注册相关的接口代码。
接口开发流程规范
我们先了解一下接口开发的流程规范,让你对项目及开发整体的流程更加熟悉。企业级的项目开发都要遵循开发规范,目的是让开发操作更规范,提高开发效率。流程规范一共有五条,我们依次来看看。
一是需求明确。在开发接口之前,我们首先要明确接口的功能需求和数据需求。这包括接口需要实现的功能、需要接收的参数以及返回的数据格式。这些如果不在开发前明确,很容易导致我们后期返工。
二是使用框架。使用现有的框架,比如Flask或Django这样的框架可以大大提高开发效率。这些框架已经为你处理了许多底层的细节,你只需要关注业务逻辑即可。我们在项目中使用的就是Flask框架。
三是设计RESTful API。RESTful是一种常用的API设计风格,它简单易用,容易理解,也方便与前端进行交互。这一部分我们在Flask-RESTful这节课已经讲过了,也为你打好了基础。
四就是进行接口测试。接口测试能够保证开发功能的实用性和完整性。通过这一步,我们就能在投入使用前提前检验我们的接口功能,保证功能顺利上线。具体就是使用Postman测试我们的API接口。这些工具无需等待前端的配合,用起来很方便。
最后是第五点,编写接口文档,这是企业里每个研发团队必须要做的规范化管理。良好的文档可以帮助其他开发者理解和使用你的接口,方便团队管理协作,也有利于接口的后期维护。
接口需求梳理
明确了开发规范,我们再结合直播视频平台的功能梳理一下接口需求的梳理,为后续开发实战做好准备。
只有理顺了接口需求,我们才能更清晰数据库表设计以及接口的功能实现方式。我们这就来看看在线视频平台里最有代表性的几个必备接口,你可以参考后面的表格来看看。
明确了功能接口的开发规范和需求情况,接下来就是实操环节。
项目接口实战
在整个后端实战课程中,我们每一个模块的学习和应用都是为了最终项目接口开发做准备。课程里我放的是核心代码,完整的代码你可以通过 Gitee链接获取。
config配置管理
首先需要搭建整体结构。我们在config文件中完成配置管理。后面是创建配置基类的代码实现。
class Config:
DEBUG = True
LEVEL_LOG = logging.INFO
SECRET_KEY = 'slajfasfjkajfj'
SQL_HOST = '127.0.0.1'
SQL_USERNAME = 'root'
SQL_PASSWORD = 'root'
SQL_PORT = 3306
SQL_DB = 'my_project'
JSON_AS_ASCII = False
# 数据库的配置
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{SQL_USERNAME}:{SQL_PASSWORD}@{SQL_HOST}:{SQL_PORT}/{SQL_DB}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
# 指定session使用什么来存储
SESSION_TYPE = 'redis'
# 指定session数据存储在后端的位置
SESSION_REDIS = StrictRedis(host=REDIS_HOST, port=REDIS_PORT)
# 是否使用secret_key签名你的sessin
SESSION_USE_SIGNER = True
# 设置过期时间,要求'SESSION_PERMANENT', True。而默认就是31天
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 # 一天有效期
完成基本配置之后,在config文件中我们还需要做不同环境的配置。配置类的作用就是提供一个分离的环境配置逻辑的机制,让我们无需修改代码,就可以在不同的环境中轻松使用不同的配置参数。这里我们需要分成测试环境、开发环境和生产环境这三种模式来设置。
首先来看测试环境配置类的具体代码。
然后是开发环境配置类具体代码。
最后是生产环境配置类具体代码。
class ProConfig(Config):
LEVEL_LOG = logging.ERROR
DEBUG = False
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:root@127.0.0.1:3306/aaa"
主程序编写
搞定配置管理以后就可以编写主程序了。我们先完成app对象的实例化,这一步是创建Flask应用实例的重要步骤,它包含了应用的各种属性和方法,用于构建 Web 应用程序。通过app对象,我们可以定义应用的路由、添加蓝图、初始化扩展等功能,进而构建出完整的应用程序。
具体做法就是在项目中创建接口api包目录,然后在__init__.py中创建app对象,具体代码如下所示。
def create_app(config_name):
app = Flask(__name__)
config = config_dict.get(config_name)
setup_log(log_file='logs/root.log', level=config.LEVEL_LOG)
app.config.from_object(config)
db.init_app(app)
global redis_store
redis_store = StrictRedis(host=config.REDIS_HOST, port=config.REDIS_PORT, decode_responses=True)
register_bp(app)
这段代码中,我们完成了后面这四步动作。
- 工厂模式下,完成不同环境下配置信息导入。
- 增加app日志管理。
- 初始化数据库,并关联app对象。
- 增加全局redis链接对象。
完成上面的操作之后,我们还需要增加注册蓝图方法,完成不同功能模块的管理。我以passport_blu模块为代表案例,带你看看具体的代码实现。其他模块增加蓝图注册方法和这里类似,只不过对应的模块名不一样。
def register_bp(app)
from api.modules.passport import passport_blu
app.register_blueprint(passport_blu)
到这里我们就完成了实例化app对象,完成了主程序的各个配置项,接下来我们就来完成moduels包的配置与开发。
创建数据库表
在这一部分我们将要完成创建passport认证模块,并且编写好登录和注册接口功能的代码。
首先我们需要在models下面的base.py文件中,创建模型基类。如何创建模型基类,我们在数据库实战那节课已经详细讲过了,这里我们直接看具体代码。
class BaseModels:
"""模型基类"""
create_time = db.Column(db.DateTime, default=datetime.now) # 创建时间
update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) # 记录你的更新时间
status = db.Column(db.SmallInteger, default=1) # 记录存活状态
def add(self, obj):
db.session.add(obj)
return session_commit()
def update(self):
return session_commit()
def delete(self):
self.status = 0
return session_commit()
完成模型基类的创建后,我们就要创建用户登录表,同样还是在models文件下的user.py文件中创建。
class UserLogin(BaseModels, db.Model):
"""用户登录表"""
__tablename__ = "user_login"
id = db.Column(db.Integer, primary_key=True, autoincrement=True) # 用户id
mobile = db.Column(db.String(16), unique=True, nullable=False) # 手机号
password_hash = db.Column(db.String(128), nullable=False) # 加密的密码
user_id = db.Column(db.Integer) # 用户id
last_login = db.Column(db.DateTime, default=datetime.now) # 最后一次登录时间
last_login_stamp = db.Column(db.Integer) # 最后一次登录时间
@property
def password(self):
raise AttributeError('密码属性不能直接获取')
@password.setter
def password(self, value):
self.password_hash = generate_password_hash(value)
# 传入的是明文,校验明文和数据库里面的hash之后密码 正确true
def check_password(self, password):
return check_password_hash(self.password_hash, password)
下一步是项目中的用户管理,在登录成功之后要展示用户信息。下面的UserInfo类主要用来创建用户信息表。
class UserInfo(BaseModels, db.Model):
"""用户信息表"""
__tablename__ = "user_info"
id = db.Column(db.Integer, primary_key=True, autoincrement=True) # 用户id
nickname = db.Column(db.String(64), nullable=False) # 用户昵称
mobile = db.Column(db.String(16)) # 手机号
avatar_url = db.Column(db.String(256)) # 用户头像路径
signature = db.Column(db.String(256)) # 签名
sex = db.Column(db.Enum('0', '1', '2'), default='0') # 1男 2 女 0 暂不填写
birth_date = db.Column(db.DateTime) # 出生日期
role_id = db.Column(db.Integer) # 角色id
is_admin = db.Column(db.SmallInteger, default=0)
last_message_read_time = db.Column(db.DateTime)
def new_messages_counts(self):
last_read_time = self.last_message_read_time or datetime(1900, 1, 1)
return Message.query.filter_by(recipient_id=self.id).filter(
Message.timestamp > last_read_time).count()
def to_dict(self):
return {
'id': self.id,
'nickname': self.nickname,
'mobile': self.mobile,
'avatar_url': self.avatar_url,
'sex': self.sex,
}
接口开发
完成了模型基类和表的创建之后,我们还要实现具体的功能接口。
我们先来梳理一下注册接口的具体功能实现。用户注册的核心逻辑就是,在用户完成一系列信息的录入后,点击注册按钮,然后将用户信息提交到数据库中。
我们直接在modules文件下的view.py文件中实现注册接口。
@passport_blu.route('/register', methods=['POST'])
def register():
"""
注册接口
:return: code msg
"""
data_dict = request.form
mobile = data_dict.get('mobile')
password = data_dict.get('password')
img_code_id = data_dict.get('img_code_id') # cur_id
img_code = data_dict.get('img_code') # 填写的code
if not all([mobile, password, img_code_id, img_code]):
return error(code=HttpCode.parmas_error, msg='注册所需参数不能为空')
# 2.1验证手机号格式
if not re.match('1[3456789]\\d{9}', mobile):
return error(code=HttpCode.parmas_error, msg='手机号格式不正确')
# 3.通过手机号取出redis中的验证码
redis_img_code = None
# 从redis取出img_code_id对应的验证码
try:
redis_img_code = redis_store.get(f'img_code: {img_code_id}')
except Exception as e:
current_app.logger.errer(e)
if not redis_img_code:
return error(HttpCode.parmas_error, 'redis图片验证码获取失败')
if img_code.lower() != redis_img_code.lower():
return error(HttpCode.parmas_error, '图片验证码不正确')
user_info = UserInfo()
user_info.mobile = mobile
user_info.nickname = mobile
user_info.add(user_info)
user_login = UserLogin()
user_login.mobile = mobile
user_login.password = password
user_login.user_id = user_info.id
user_login.add(user_login)
return success('注册成功')
在前面这段代码中,通过request.form获取到用户信息之后,我们分别进行了表单非空验证、手机号格式认证以及图片验证码的认证。用户必须完成以上认证之后,才可以完成注册。
我们需要特别留意一下图片验证码的实现。这里我们主要借助三方包来生成简单的验证码接口。
@passport_blu.route('/image_code')
def img_code():
"""
生成图像验证码
:return: 图片的响应
"""
# 1.获取请求参数,args是获取?后面的参数
cur_id = request.args.get('cur_id')
pre_id = request.args.get('pre_id')
# 2.生成图片验证码
name, text, img_data = captcha.captcha.generate_captcha()
# 3.保存到redis
try:
redis_store.set(f'img_code: {cur_id}', text, IMAGE_CODE_REDIS_EXPIRES)
# 判断是否有上一个uuid,如果存在则删除
if pre_id:
redis_store.delete(f'img_code: {pre_id}')
except Exception as e:
current_app.logger.error(e)
return error(HttpCode.db_error, 'redis存储失败')
# 4. 返回图片验证码
response = make_response(img_data)
response.headers["Content-Type"] = "image/jpg"
return response
前面这段代码的作用是生成一个验证码,并将其存储在变量 name、text 和 img_data 中。通过调用captcha.captcha.generate_captcha()方法(这是一个生成验证码的函数),返回一个元组 (name, text, img_data)。其中name是验证码的名称,text是验证码的文本,img_data是验证码的图像数据。
当然,我们也不能忽略后面对应的异常处理,这样才能保证程序稳定执行。
用户注册接口的开发中,有个非常重要的功能——用户判重。
比方说,一个手机号我们不支持多次注册。这一步实现的逻辑是这样的:我们要在用户点击注册或完成手机号输入之后,就通过查询现有用户手机号来判断是否重合。如果查询到相同的则注册失败,相反直接注册成功。你可以结合后面的代码实现来加深理解。
@passport_blu.route('/check_mobile', methods=['POST'])
def check_mobile():
"""
验证手机号
# 请求路径: /passport/check_mobile
# 请求方式: POST
# 请求参数: mobile
:return:code,msg
"""
data_dict = request.form
mobile = data_dict.get('mobile')
try:
users = UserLogin.query.all()
except Exception as e:
current_app.logger.error(e)
return error(code=HttpCode.db_error, msg='查询用户信息异常')
if mobile in [i.mobile for i in users]:
return error(code=HttpCode.parmas_error, msg='手机号已存在,请重新输入')
return success(msg=f'{mobile},此手机号可以使用')
这段代码中,最核心的部分就是在获取到mobile之后,通过UserLogin.query.all()方法查询数据,根据返回的数据来判断注册手机号。
到这里我们就完成了用户的注册功能,至于登录功能。我们上节课讲认证机制的时候已经详细说过,从生成Token到用户鉴权的全过程相信你已经非常熟悉了,完整代码你同样可以参考 Gitee。
总结
又到了课程的尾声,接下来我们一起对这节课学到的内容做个总结。
前面的课程中我们或多或少的都有涉及到接口实现的方法,这节课我们以用户注册的接口实现为例,更加体系化地实现了完整的功能开发。
具体实战前,我们先梳理了开发流程规范和接口需求。规范的流程不但能提高效率,也能更好地实现团队合作。接口需求能让我们明确之后要做哪些功能,为之后的实现环节做好预热。
接口开发阶段,我们从config项目配置管理到主程序编写,你必须要掌握实例化app对象的创建代码中每一项的作用。而在模型基类和用户表创建这一部分里,我们要注意提前梳理好每一模块的字段信息以及对应的字段类型。接口实现过程中一定要注意有业务逻辑实现和异常处理,只有全面考虑,才能保证程序的稳定执行。
这节课,我们以注册接口为案例带你体验了接口系统化开发的过程。用户相关的接口开发也是一样的实现方法,相信你在掌握了注册的接口实现之后,应对其他用户相关的接口实现也会非常轻松。希望你课后对照配套代码多多练习,巩固学习效果。
思考题
在课程中注册的时候我们做了图形验证,如果通过短信认证的方式来实现注册,你有什么好的想法分享么?
欢迎你在留言区和我交流互动,如果这节课对你有启发,别忘了分享给身边更多朋友。
- 胡超 👍(0) 💬(3)
建议在每段代码中针对需要引入的模块,import下或者form * import *下,不然有时会看点有点模糊,打个比方:name, text, img_data = captcha.captcha.generate_captcha()其中captchp引入的是哪个模块等,这样会有助于阅读代码,谢谢🙏
2023-09-18