async def wrap_data(self, key) -> CachedDataWrapper: wrapper = await super().wrap_data(key) if wrapper and wrapper.data: # 对数据进行重新包装,将文件内容换成 json 对象 wrapper.data = json.loads(wrapper.data) Cms4pyLog.get_instance().debug(f"Load json file {key}") return wrapper
async def __call__(self, *args, **kwargs): # 获取数据管理器实例 db_manager = await Db.get_instance() # 通过连接池建立一个连接 conn = await db_manager.async_pydal.acquire() # 自动提交数据更改 await conn.autocommit(True) # 获取数据库对象用于操作数据库 self.db = await conn.cursor() # 无论在execute中发生了什么错误,总要执行释放连接的操作 err = None try: await self.execute(*args, **kwargs) except BaseException as e: err = e Cms4pyLog.get_instance().error(e) # 关闭数据库对象 await self.db.close() # 释放连接 await db_manager.async_pydal.release(conn) if err: # 如果在执行 execute 过程中发生了错误,将此错误抛给ASGI raise err
async def application(scope, receive, send): # 获取请求类型 request_type = scope['type'] # 如果是 http 类型的请求,则由该程序段处理 if request_type == 'http': # 如果路径以 /socket.io 开始,则使用 socket.io app 进行处理 if scope['path'].startswith("/socket.io"): return await sio_asgi_app(scope, receive, send) data_sent = await dynamic_handler.handle_dynamic_request( scope, receive, send) # 如果经过动态请求处理程序后未发送数据,则尝试使用静态文件请求 # 处理程序处理 if not data_sent: data_sent = await static_file_handler.handle_static_file_request( scope, send) # 如果静态文件处理程序未发送数据,则意味着文件找不到,此时应该 # 向浏览器发送404页面 if not data_sent: # 对于未被处理的请求,均向浏览器发回 404 错误 await error_pages.send_404_error(scope, receive, send) # 如果是 websocket,则使用 socket.io app 处理 elif request_type == 'websocket': return await sio_asgi_app(scope, receive, send) # 如果是生命周期类型的请求,则由该程序段处理 elif request_type == 'lifespan': await lifespan_handler.handle_lifespan(scope, receive, send) else: Cms4pyLog.get_instance().warning("Unsupported ASGI type")
async def wrap_data_callback(cache_key) -> CachedDataWrapper: await target(*args) Cms4pyLog.get_instance().debug(f"Cache page {cache_key}") return CachedDataWrapper( res.body, # 记录缓存过期时间,便于对比 datetime.datetime.now().timestamp() + expire )
async def wrap_data(self, key) -> CachedDataWrapper: m = None t = 0 try: # 导入模块 m = importlib.import_module(key) m_file = self.file_name_from_module_name(key) # 获取文件时间戳 t = await aiofile.getmtime(m_file) except ModuleNotFoundError: Cms4pyLog.get_instance().warning(f"Module {key} not found") return CachedDataWrapper(m, t)
async def handle_lifespan(scope, receive, send): while True: # 不断读取数据 message = await receive() # 如果读取消息类型为 lifespan.startup,则进行初始化操作 if message['type'] == 'lifespan.startup': await load_sio_files() # 在初始化完成后,向 asgi 环境发送启动完成消息 await send({'type': 'lifespan.startup.complete'}) Cms4pyLog.get_instance().info("Server started") # 如果读取消息类型为 lifespan.shutdown,则进行收尾工作 elif message['type'] == 'lifespan.shutdown': # 在收尾工作结束后,向 asgi 环境发送收尾完成消息 await send({'type': 'lifespan.shutdown.complete'}) Cms4pyLog.get_instance().info("Server stopped") break
async def _parse_form(self): """ 解析表单,该函数由cms4py框架内部调用,应用层不应该调用此函数 :return: """ # TODO 目前实现仅支持GET方法与POST方法,需要逐步完善 # 并支持所有的 HTTP 方法 if self.query_string: # 尝试从 URL 中解析参数 self._query_vars = url_helper.parse_url_pairs( self.query_string ) if self.method == "POST": # 如果是 POST 方式,尝试读取消息体 while True: message = await self._receive() # TODO 需要实现数据限制机制以防攻击 self._body += message["body"] if 'body' in message else b'' if "more_body" not in message or not message["more_body"]: break if self.content_type: # 如果是 application/x-www-form-urlencoded 编码方式, # 则尝试以 URL 参数对的方式解析 if self.content_type.startswith( b'application/x-www-form-urlencoded' ): self._body_vars = url_helper.parse_url_pairs( self._body ) # 如果是 multipart/form-data 则当成表单数据解析,可用 # 于处理文件上传请求 elif self.content_type.startswith(b"multipart/form-data"): # 该正则用于取出数据分割符 boundary_search_result = re.search( b"multipart/form-data; boundary=(.+)", self.content_type ) if boundary_search_result: # 取出分割符 boundary = boundary_search_result.group(1) if self._body: # 用分割符分割表单数据 body_results = self.body.split( b'\r\n--' + boundary ) if body_results: for body_result in body_results: # 分割后的每一条数据都有头部和内容, # 头部和内容以 \r\n\r\n 分开, # 头部是字符串,描述该数据的信息 # 内容部分是二进制数据 split_index = body_result \ .find(b'\r\n\r\n') if split_index != -1: # 获取头部信息字符串 head = body_result[:split_index] # 获取内容 content = body_result[ split_index + 4:] # 取出该字段的名称 name_result = re.search( b'Content-Disposition: form-data; name="([^"]+)"', head, re.M ) if name_result: name = name_result.group(1) if name not in self._body_vars: self._body_vars[name] = [] # 如果是文件,则取出该字段的文件名 file_name_result = re.search( b' filename="([^"]+)"', head, re.M ) file_name = file_name_result \ .group(1) if \ file_name_result else None if not file_name: # 如果不是文件,值为普通字符串 self._body_vars[name] \ .append(content) else: file_object = { 'name': name, 'filename': file_name, 'content': content } content_type_result = \ re.search( b'Content-Type: (.*)', head, re.M ) if content_type_result: file_object[ 'content-type' ] = content_type_result.group(1) # 如果是文件,则值为文件对象 self._body_vars[name].append(file_object) pass else: break else: Cms4pyLog.get_instance().info( f"Request content-type is {self.content_type}, we do not parse" ) else: Cms4pyLog.get_instance().warning("content-type is None") pass pass