Flaskz介绍3--API封装、访问权限控制和日志
开发过程中,最多的路由API就是对于数据的增删改查操作,Flaskz提供了封装函数,用于生成数据模型的CRUD等路由函数,可以方便地生成数据操作的相关API,并提供了访问权限控制和日志记录功能。
注册数据模型路由
通过register_model_route函数,可以生成数据库模型的CRUD等操作对应的API,下面先介绍一下这个函数。
register_model_route(app, model, rule, module=None, types=None, multi_models=None, to_json_option=None, strict_slash=True)
- 参数:
- {Blueprint/Flask}app --指定的Flask应用/Blueprint实例,用于注册API
- {BaseModelMixin}model --数据模型类,例如User/Device等
- {str}rule --路由的URL路径,例如"users","/devices"等
- {str}module --API所属的模块,比如"users"/"devices",主要用于权限控制和记录日志
- 如果API没有所属模块,可以设置module为None
- 如果设置module为False,则API没有任何权限控制(不需要登录也可以访问)
- {list}types --生成的路由列表,默认会生成以下路由API
类型 选项 Method URL 说明 添加 add POST http://{{flask_server_url}}/api/v1.0/{rule}/ 通过POST方法,添加模型数据 删除 delete DELETE http://{{flask_server_url}}/api/v1.0/{rule}/[pk_value]/ 通过DELETE方法,删除指定主键值对应的模型数据 修改 update PATCH http://{{flask_server_url}}/api/v1.0/{rule}/ 通过PATCH方法,修改指定的模型数据 修改或添加 upsert POST http://{{flask_server_url}}/api/v1.0/{rule}/upsert/ 通过POST方法,数据存在则进行修改,不存在则添加 查询 query GET http://{{flask_server_url}}/api/v1.0/{rule}/ 查询模型类所有的数据 条件查询 pss POST http://{{flask_server_url}}/api/v1.0/{rule}/pss/ 根据指定条件(分页+搜索+排序)查询符合条件的模型数据 多表查询 multi GET http://{{flask_server_url}}/api/v1.0/{rule}/multi/ 一次查询多个模型/数据库表的所有数据 - {dict|None}to_json_option --模型类instance转换为json的选项, 请参考 model_to_dict 函数和 BaseModelMixin.to_dict 方法
- {dict|None}multi_models --用于生成多表查询API,一次查询多个模型类的数据,如果没有multi_models参数,则不会生成多表查询路由API, 请参考 query_multiple_model 函数
- 示例:
# 生成User相关的API # -查询所有数据 [GET] http://{{flask_server_url}}/api/v1.0/users/ # -增加数据 [POST] http://{{flask_server_url}}/api/v1.0/users/ # -根据主键删除数据 [DELETE] http://{{flask_server_url}}/api/v1.0/users/[id] # -更新数据 [Patch] http://{{flask_server_url}}/api/v1.0/users/ # -条件查询 [POST] http://{{flask_server_url}}/api/v1.0/user/pss/ # -多表查询 [GET] http://{{flask_server_url}}/sys_mgmt/user/multi/ register_model_route(api_bp, User, 'users', 'users',multi_models={ 'users': User, 'roles': { 'model': Role, 'to_json_option': { # 'cascade': 2, # 级联关系层级 # 'relationships': ['department'], # 指定输出的级联关系 'include': ['id', 'name'] } } }) #其它示例 register_model_route(sys_mgmt_bp,ActionLog 'action-logs', 'action-logs', types=['pss']) # 不需要增删改操作,只生成query_pss查询API
请参考API Postman示例
- 说明:
- 程序生成的API请参考API规范,和Rest规范相比,主要有以下两个差异差异
- 修改数据API中用到的主键值,放在了body数据中而不是url
--[Patch] http://{{flask_server_url}}/api/v1.0/user/
- 另外为了更方便的组织查询条件数据,条件查询用的也是POST方法,
即所有的分页/过滤/排序参数都要放在请求body中,请参考 BaseModelMixin.query_pss 和 parse_pss 函数
--[POST] http://{{flask_server_url}}/api/v1.0/user/pss/
- 修改数据API中用到的主键值,放在了body数据中而不是url
- 在生成的API处理函数内部,通过flaskz_logger进行了系统日志记录,以方便程序的跟踪调试
- 查询相关API中,使用debug方法记录日志
- 其他API中,使用info方法记录日志
- 除了记录系统日志,API内部还通过log_operation记录了操作日志(插入记录到数据库日志表中),以方便后续查询
- 生成的API都添加了模块和操作权限控制,参考以下权限控制和操作日志记录章节
- 程序生成的API请参考API规范,和Rest规范相比,主要有以下两个差异差异
除了register_model_route,还有以下路由注册函数,具体使用请参考函数文档。
- register_model_add_route --可用于生成指定数据模型的添加路由函数
- register_model_delete_route --可用于生成指定数据模型的删除路由函数
- register_model_update_route --可用于生成指定数据模型的更新路由函数
- register_model_upsert_route --可用于生成指定数据模型的添加/更新路由函数
- register_model_query_route --可用于生成指定数据模型的全量查询路由函数
- register_model_query_pss_route --可用于生成指定数据模型的条件查询(分页+搜索+排序)路由函数
- register_multi_models_query_route --可用于生成多个数据模型的全量查询路由函数
查询API添加过滤
在查询相关API中,可以对结果数据进行过滤
- 全量查询 / [GET]http://{{flask_server_url}}/api/v1.0/{url_prefix}/
register_model_route(api_bp, EmployeeModel, 'ex-employees', 'ex-employees', to_json_option={ # **在to_json_option选项中添加filter函数 'filter': lambda ins: ins.age >= 20 # filter过滤函数, 判断结果==True的才会返回 })
- 条件查询 / [POST]http://{{flask_server_url}}/api/v1.0/{rule}/pss/
def append_search_config(request_json): if request_json: request_json.get('search', {})['age'] = { # 在request_json中附加查询条件 ">": 20 } return request_json register_model_query_pss_route(api_bp, EmployeeModel, 'ex-employees', 'ex-employees', get_pss_config=append_search_config) # 通过设置get_pss_config回调函数以自定义pss查询参数
- 多表查询 / [GET]http://{{flask_server_url}}/api/v1.0/{rule}/multi/
register_model_route(api_bp, EmployeeModel, 'ex-employees', 'ex-employees' multi_models={ 'employees': { 'model': EmployeeModel, 'option': { 'filter': lambda ins: ins.age >= 20 # **在multi_models的option选项中设置filter函数函数 } }, 'departments': { 'model': DepartmentModel, 'option': { 'include': ['id', 'name'] } } })
注册数据模型批量操作路由
通过register_model_bulk_route函数,可以生成数据库模型的批量增删改操作对应的API,下面先介绍一下这个函数。
register_model_bulk_route(app, model, rule, module=None, types=None, with_relationship=False, strict_slash=True)
- 参数:
- {Blueprint/Flask}app --指定的Flask应用/Blueprint实例,用于注册API
- {BaseModelMixin}model --数据模型类,例如User/Device等
- {str}rule --路由的URL路径,例如"users","/devices"等
- {str}module --API所属的模块,比如"users"/"devices",主要用于权限控制和记录日志
- 如果API没有所属模块,可以设置module为None
- 如果设置module为False,则API没有任何权限控制(不需要登录也可以访问)
- {list}types --生成的路由列表,默认会生成以下路由API
类型 选项 Method URL 说明 批量添加 bulk_add POST http://{{flask_server_url}}/api/v1.0/{rule}/bulk/ 通过POST方法,批量添加模型数据 批量删除 bulk_delete DELETE http://{{flask_server_url}}/api/v1.0/{rule}/bulk/[pks]/ 通过DELETE方法,批量删除通过请求的URL或body中指定的主键列表 批量修改 bulk_update PATCH http://{{flask_server_url}}/api/v1.0/{rule}/bulk/ 通过PATCH方法,批量修改指定的模型数据 - {boolean}with_relationship --如果with_relationship=True,则通过API进行批量操作时,会更新关系数据
- 示例:
# 生成User批量操作的API # -批量增加数据 [POST] http://{{flask_server_url}}/api/v1.0/users/bulk/ # -批量删除数据 [DELETE] http://{{flask_server_url}}/api/v1.0/users/bulk/[ids]/ # -批量更新数据 [Patch] http://{{flask_server_url}}/api/v1.0/users/bulk/ register_model_bulk_route(api_bp, User, 'users', 'users') #其它示例 register_model_bulk_route(sys_mgmt_bp,User 'users', 'users', types=['bulk_add']) # 不需要删改操作,只生成批量添加API
请参考API Postman示例
register_model_bulk_route,还有以下路由注册函数,具体使用请参考函数文档。
- register_model_bulk_add_route --可用于生成指定数据模型的批量添加路由函数
- register_model_bulk_delete_route --可用于生成指定数据模型的批量删除路由函数
- register_model_bulk_update_route --可用于生成指定数据模型的批量更新路由函数
创建数据模型API
建议使用register_model_route函数生成模型路由
通过init_model_rest_blueprint函数,可以将数据库模型的增删改查操作封装成API,下面先介绍一下这个函数。
init_model_rest_blueprint(model_cls, api_blueprint, url_prefix, module, routers=None, to_json_option=None, multiple_option=None)
- 参数:
- {BaseModelMixin}model_cls --数据模型类,例如User/Device等
- {Blueprint}api_blueprint --指定的API Blueprint实例,用于注册API
- {string}url_prefix --API对应的URL前缀,用于生成API的URL,例如"/user","/device"等
- {string}module --API所属的模块,比如"user"/"device",主要用于权限控制和记录日志
- 如果API没有所属模块,可以设置module为None
- 如果设置module为False,则API没有任何权限控制(不需要登录也可以访问)
- {list}routers --需要生成的API列表,默认会生成以下API
类型 选项 Method URL 说明 添加 add POST http://{{flask_server_url}}/api/v1.0/{url_prefix}/ 通过POST方法,添加模型数据 删除 delete DELETE http://{{flask_server_url}}/api/v1.0/{url_prefix}/[pk_value] 通过DELETE方法,删除指定主键值对应的模型数据 修改 update PATCH http://{{flask_server_url}}/api/v1.0/{url_prefix}/ 通过PATCH方法,修改指定的模型数据 修改或添加 upsert POST http://{{flask_server_url}}/api/v1.0/{url_prefix}/upsert/ 通过POST方法,数据存在则进行修改,不存在则添加 查询 query GET http://{{flask_server_url}}/api/v1.0/{url_prefix}/ 查询模型类所有的数据 条件查询 query_pss POST http://{{flask_server_url}}/api/v1.0/{url_prefix}/query_pss/ 根据条件查询符合条件的模型数据 多表查询 query_multiple GET http://{{flask_server_url}}/sys_mgmt/{url_prefix}/query_multiple/ 一次查询多个模型/数据库表的所有数据 - {dict|None}to_json_option --模型类instance转换为json的选项, 请参考 model_to_dict 函数和 BaseModelMixin.to_dict 方法
- {dict|None}multiple_option --主要用于生成query_multiple API,用于一次查询多个模型类的数据,如果没有multiple_option参数,则不会生成query_multiple API, 请参考 query_multiple_model 函数
- 示例:
# 生成User相关的API # -查询所有User数据 [GET] http://{{flask_server_url}}/api/v1.0/user/ # -增加数据 [POST] http://{{flask_server_url}}/api/v1.0/user/ # -根据id删除数据 [DELETE] http://{{flask_server_url}}/api/v1.0/user/[id] # -更新数据 [Patch] http://{{flask_server_url}}/api/v1.0/user/ # -根据条件查询数据 [POST] http://{{flask_server_url}}/api/v1.0/user/query_pss/ # -查询User和Role数据 [GET] http://{{flask_server_url}}/sys_mgmt/user/query_multiple/ init_model_rest_blueprint(User, api_bp, '/user', 'user',multiple_option={ 'users': User, 'roles': { 'model_cls': Role, 'option': { # 'cascade': 2, 'include': ['id', 'name'] } } }) #其它示例 init_model_rest_blueprint(OPLog, sys_mgmt_bp, '/op_log', 'op_log', routers=['query_pss']) # 不需要增删改操作,只生成query_pss查询API
请参考API Postman示例
- 说明:
- 程序生成的API请参考API规范,和Rest规范相比,主要有以下两个差异差异
- 修改数据API中用到的主键值,放在了body数据中而不是url
--[Patch] http://{{flask_server_url}}/api/v1.0/user/
- 另外为了更方便的组织查询条件数据,条件查询用的也是POST方法,
即所有的分页/过滤/排序参数都要放在请求body中,请参考 BaseModelMixin.query_pss 和 parse_pss 函数
--[POST] http://{{flask_server_url}}/api/v1.0/user/query_pss/
- 修改数据API中用到的主键值,放在了body数据中而不是url
- 在生成的API处理函数内部,通过flaskz_logger进行了系统日志记录,以方便程序的跟踪调试
- 查询相关API中,使用debug方法记录日志
- 其他API中,使用info方法记录日志
- 除了记录系统日志,API内部还通过log_operation记录了操作日志(插入记录到数据库日志表中),以方便后续查询
- 生成的API都添加了模块和操作权限控制,参考以下权限控制和操作日志记录章节
- 程序生成的API请参考API规范,和Rest规范相比,主要有以下两个差异差异
API权限控制和操作日志
系统提供了 ModelRestManager 类对API进行权限控制和操作日志记录。
初始化
不同项目中,对于权限控制和记录操作日志实现差异较大,所以默认不提供,如果需要,可以通过设置回调函数的方式来实现具体的功能逻辑,请参考Flaskz-admin管理系统模板。
示例代码如下
model_rest_manager = ModelRestManager() # 创建ModelRestManager实例
model_rest_manager.login_check(auth.login_check) # 设置用户是否登录检查回调函数,对应rest_login_required装饰器
model_rest_manager.permission_check(auth.permission_check) # 设置权限检查回调函数,对应rest_permission_required装饰器
model_rest_manager.logging(sys_mgmt.log_operation) # 设置操作日志记录函数,对应log_operation函数
model_rest_manager.init_app(app) # 将ModelRestManager实例绑定到应用上
权限控制
设置好model_rest_manager以后,就可以通过封装好的函数装饰器来设置权限控制功能了
- rest_login_required 函数装饰器 --控制API只有登录用户才可以访问
- rest_permission_required(module,operation) 函数装饰器 --控制API只能被有模块访问权限&模块对应操作权限的用户才可以访问
示例代码
@sys_mgmt_bp.route('/auth/account/', methods=['GET', 'POST'])
@rest_login_required() # *只有已登录用户,才可以调用/auth/account/接口API加载当前账户信息
def sys_auth_account_query():
pass
@sys_mgmt_bp.route('/role/', methods=['POST'])
@rest_permission_required('role', 'add') # *只有已登录用户,并且拥有模块访问&添加权限,才可以调用API添加角色数据
def sys_role_add():
#...
log_operation('role', 'add', result[0], req_log_data, res_log_data) # 记录操作日志-->数据库
flaskz_logger.info(get_rest_log_msg('Add role', req_log_data, result[0], res_log_data)) # 记录系统日志-->文件/控制台
pass
操作日志
初始化model_rest_manager以后,就可以通过封装好的函数来记录操作日志日志
- log_operation(*args, **kwargs) 函数 --记录操作日志
示例代码
@sys_mgmt_bp.route('/role/', methods=['POST'])
@rest_permission_required('role', 'add')
def sys_role_add():
#...
log_operation('role', 'add', result[0], req_log_data, res_log_data) # 记录操作日志-->数据库
flaskz_logger.info(get_rest_log_msg('Add role', req_log_data, result[0], res_log_data)) # 记录系统日志-->文件/控制台
pass
自定义API
除了通过init_model_rest_blueprint函数生成模型对应的API以外,也可以根据需求和场景自定义API,下面通过一个示例,演示如何自定义API
@sys_mgmt_bp.route('/role/', methods=['PUT', 'PATCH']) # 向blueprint注册api,请求url为/role/,请求方法为PUT或PATCH
@rest_permission_required('role', 'update') # 模块权限和操作权限控制
def sys_role_update(): # 路由处理函数
"""
Update the specified role.
:return:
"""
request_json = request.json
req_log_data = json.dumps(request_json)
result = Role.update(Role.to_server_json(request_json)) # 更新数据库
res_data = model_to_dict(result[1], {'cascade': 1}) # 将更新以后的数据转换为字典属性对象
if result[0] is True: # 对操作成功/失败进行判断
res_data = Role.to_client_json(res_data)
res_log_data = get_log_data(res_data)
log_operation('role', 'update', result[0], req_log_data, res_log_data) # 记录操作日志
flaskz_logger.info(get_rest_log_msg('Update role', req_log_data, result[0], res_log_data)) # 记录系统日志
return create_response(result[0], res_data) # 将结果返回到请求客户端
请求转发
可以将收到的API请求转发到其他系统,可以用于以下场景
- 系统整合,将多个系统整合到一个系统
- 跨域访问,解决有些系统不支持跨域,导致的浏览器跨域访问问题
实现如下
class RegexConverter(PathConverter):
def __init__(self, url_map, regex):
super(RegexConverter, self).__init__(url_map)
self.regex = regex
app.url_map.converters['regex'] = RegexConverter # 添加正则类型的路由参数类型
@api_bp.route('/<regex(".*"):path>', methods=HTTP_METHODS) # 处理所有/特定请求path
def remote(path):
return forward_request(base_url + path) # 转发请求并返回,原请求的'method', 'data', 'json', 'headers', 'cookies'都会被转发
系统日志
系统提供了系统日志记录功能,以监控程序的运行和跟踪调试,生成的日志记录器名字为"flaskz_logger"。
配置&初始化
可以通过init_log函数进行配置,相关配置参数如下
- FLASKZ_LOGGER_FILENAME
系统日志的文件名,只有设置了FLASKZ_LOGGER_FILENAME,系统日志才会输出到文件中,否则系统日志会被输出到控制台,默认为空。
示例,FLASKZ_LOGGER_FILENAME = 'syslog.txt',表示系统日志会被记录到syslog.txt文件中
- 开发环境,可以把系统日志可以直接输出到控制台,以便于调试
- 测试/产品环境,要把系统日志输出到文件中,以跟踪程序的运行状态和异常处理
请参考Flaskz-admin中的不同环境的配置信息&系统日志查询功能
- FLASKZ_LOGGER_FILEPATH
系统日志的目录,如果指定了FLASKZ_LOGGER_FILENAME,默认会将日志保存到当前应用的syslog目录。
示例,FLASKZ_LOGGER_FILEPATH = os.path.join(os.getcwd(), './syslog'),表示所有日志文件会被放到syslog目录中。
- FLASKZ_LOGGER_LEVEL
系统日志的level,可选值为CRITICAL/ERROR/WARNING/INFO/DEBUG/NOTSET,请参考 logging
输出的日志,只有级别大于或等于设置的日志类型才会被记录
Flaskz中,使用的日志记录规则如下
- 用info方法记录增删改操作
- 用debug方法记录查询操作
- 用exception方法记录异常信息
- FLASKZ_LOGGER_FORMAT
日志内容输出格式,例如,FLASKZ_LOGGER_FORMAT = '%(asctime)s %(filename)16s[line:%(lineno)-3d] %(levelname)8s: \n%(message)s\n'
- FLASKZ_LOGGER_WHEN
文件型日志的rollover时间,例如,FLASKZ_LOGGER_WHEN = 'midnight'
- FLASKZ_LOGGER_BACKUP_COUNT
文件型日志最大的保存数量,例如,FLASKZ_LOGGER_BACKUP_COUNT = 90
- FLASKZ_LOGGER_DISABLED
是否禁用flaskz_logger日志管理器,如果禁用,flaskz_logger日志管理器则不会输出日志,默认不禁用
- FLASKZ_WZ_LOGGER_DISABLED
是否禁用werkzeug日志管理器,如果禁用,werkzeug日志管理器则不会输出日志,默认禁用
以上配置参数,请参考 Flaskz-admin 模板。
可以通过flaskz.flaskz_logger,使用日志记录器来记录日志,示例如下
log.init_log(app) # 初始化flaskz日志管理器,一般在创建应用时调用
flaskz_logger.info(get_rest_log_msg('Update role', req_log_data, result[0], res_log_data)) # 记录日志
系统日志vs操作日志
系统日志和操作日志是两个不同的概念,用途也不一样,主要区别如下
- 系统日志 --供开发人员使用,用于程序的跟踪调试和监控程序的运行状态,一般保存到文件或打印到控制台
- 操作日志 --供系统的使用者使用,用于对业务相关的跟踪和审计等功能,一般保存到数据库中