- 场景分析
使用python flask开发web系统,该系统是基于用户认证鉴权的web系统, 系统中涉及到关键数据的操作,因此需要针对业务操作进行记录(也就是审计日志),便于管理员后期查看,在基于java的Spring系列框架中我们可以借助于AOP面向切面的编程来完成,在使用Flask时可以借助于请求钩子 @app.before_request + @app.after_request 来实现,关键难点在于如何保证记录的是同一个请求URL(一个是请求前预记录,一个是请求后根据返回码进行补充)
- 代码实现
- 如何实现预记录+修改补充,在预记录时生成一个唯一性UUID标记本次URL请求
- 尝试一: 使用request.headers中扩充携带UUID,失败,报错说headers是不可变的
- 尝试二: 使用request.session中扩充携带UUID,与上面类似
- 尝试三: 通过HashMap携带,在后期分布式应用中不一定可靠
- 尝试四: 通过请参数中扩展,存在的问题 如何在PUT、POST、GET、DELETE不同请求方式中实现
- 定义数据库实体类
# 业务审计日志表 class BusinessAuditLog(db.Model): __tablename__ = 'business_aduit_record' id = db.Column(db.Integer, primary_key=True, autoincrement=True) uuid = db.Column(db.String(50)) url = db.Column(db.String(50)) req_params = db.Column(db.String(5000)) req_method = db.Column(db.String(10)) is_succ = db.Column(db.String(10)) cost_time = db.Column(db.Integer) resp_code = db.Column(db.String(10)) resp_data = db.Column(db.Text(5000)) resp_msg = db.Column(db.String(500)) user_id = db.Column(db.Integer, db.ForeignKey("user.id")) start_time = db.Column(db.DateTime(50), default=datetime.now) end_time = db.Column(db.DateTime(50), default=datetime.now) def __int__(self, url, req_params, req_method, user_id, start_time, uuid): self.url = url self.req_params = req_params self.req_method = req_method self.user_id = user_id self.start_time = start_time self.uuid = uuid
-
- 请求前预记录(携带token校验代码)
# 不需要验证的接口地址 no_use_auth_urls = ["/admin/user/logout", "/admin/user/login", "/admin/user/register", "/admin/user/code", "/admin/menu/import", "/admin/menu/tree", "/admin/menu/tree/i18n", "/favicon.ico"] # 在每一次请求之前调用,这时候已经有了请求,可以在这个方法里面做请求的校验 # 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数了 目前必须在这个文件中才会执行 @app.before_request def before_request(): url = request.url base_url = request.base_url root_url = request.root_url host_url = request.host_url full_path = request.full_path f_path = full_path.split("?") bus_url = str(f_path[0]) print("====================== ", bus_url) is_static = bus_url.startswith("/static") if bus_url not in no_use_auth_urls and is_static == False: print('before_request bus_url : ', bus_url) token = request.headers.get('Authorization') user_id = UserToken.get_user_id_from_token(token) if token is None: return JsonResponse.fail(9999, 'request headers missing Authorization') if not RedisUtil.validate_token(token): return JsonResponse.fail(9401, 'request headers missing Authorization or Authorization is expired,need to login') # 对于系统相关业务的GET请求不记录 method = request.method if bus_url.startswith("/admin/") and method == 'GET': return None if is_static: return None req_uuid = str(uuid.uuid4()) req_data = str(request.data, encoding='utf-8') start_time = time.time() start_time_hm = int(round(start_time * 1000))
# GET/DELETE请求方式中存在的情况 if req_data is None or CommonTool.is_none(req_data) or req_data == '{}': req_data = "{\"REQ_UUID\": \"" + req_uuid + "\",\"START_TIME\": " + str(start_time_hm) + "}" else: req_data_dict = json.loads(req_data) req_data_dict['REQ_UUID'] = req_uuid req_data_dict['START_TIME'] = str(start_time_hm) req_data = json.dumps(req_data_dict) req_data_b = req_data.encode(encoding='utf-8') request.data = req_data_b req_params = None if method == 'DELETE' or method == 'GET': req_params = request.args.to_dict().__str__() else: req_params = request.get_data(as_text=True) db.session.add(BusinessAuditLog(url=bus_url, req_params=req_params, req_method=method, user_id=user_id, start_time=datetime.now(), uuid=req_uuid)) db.session.commit() return None
-
- 请求后补充更新
# 在执行完视图函数之后调用,并且会把视图函数生成的响应传入,故其函数需要有一个response参数,可以在此方法中对响应进行进一步处理 @app.after_request def after_request(response): full_path = request.full_path f_path = full_path.split("?") bus_url = str(f_path[0]) is_static = bus_url.startswith("/static") if is_static or bus_url in no_use_auth_urls: return response response.headers['Content-Type'] = 'application/json' res = response.json resp_code = int(res['code']) req_data = request.data if resp_code != 9401 and CommonTool.is_not_none(req_data) and req_data is not None: resp_msg = res['msg'] resp_data = res['data'] req_data = str(request.data, encoding='utf-8') req_data_json = json.loads(req_data) req_uuid = req_data_json['REQ_UUID'] start_time_hm = req_data_json['START_TIME'] end_time = time.time() end_time_hm = int(round(end_time * 1000)) method = request.method cost_time = end_time_hm - int(start_time_hm) is_succ = None if resp_code == 200: is_succ = "Y" else: is_succ = "N" BusinessAuditLog.query.filter(BusinessAuditLog.uuid == req_uuid, BusinessAuditLog.req_method == method, BusinessAuditLog.url == bus_url ).update( { "end_time": datetime.now(), "cost_time": cost_time, "resp_code": resp_code, "resp_data": json.dumps(resp_data), "resp_msg": resp_msg, "is_succ": is_succ } ) db.session.commit() return response
标签:03,python,request,req,db,flask,url,time,data From: https://www.cnblogs.com/dduo/p/17401063.html