如何实现Quart应用在后台任务完成后自动重定向用户?
实现Quart后台任务完成后自动跳转页面
因为HTTP是无状态协议,后台任务无法主动触发前端页面的重定向,所以需要通过前端主动查询状态或者WebSocket实时推送两种方式实现,下面是具体方案:
方案一:前端轮询(简单易实现)
1. 新增任务状态存储
用内存字典存储任务状态(单进程部署可用,多进程/分布式建议用Redis):
from uuid import uuid4 # 存储任务状态:key为task_id,value为{"status": "pending"/"completed", "user_id": xxx} task_status = {}
2. 修改首页路由,生成任务ID并传递
@app.route('/', methods=['POST']) # 注意指定POST方法,适配表单提交 async def index(): form = await request.form user_id = int(form['user_id']) # 生成唯一任务ID task_id = str(uuid4()) # 初始化任务状态为待完成 task_status[task_id] = {"status": "pending", "user_id": user_id} # 把task_id传给后台任务 app.add_background_task(task_to_do, user_id=user_id, task_id=task_id) # 重定向到加载页面并携带task_id return redirect(f'/loading?task_id={task_id}')
3. 修改后台任务,完成后更新状态
async def task_to_do(user_id: int, task_id: str): # 模拟耗时抓取操作,替换为实际抓取逻辑 await asyncio.sleep(60) # 抓取完成后更新任务状态 task_status[task_id]["status"] = "completed" # 如需存储抓取结果,可在此添加字段,比如 task_status[task_id]["result"] = 抓取数据
4. 新增任务状态查询接口
@app.route('/check-task-status') async def check_task_status(): task_id = request.args.get('task_id') if not task_id or task_id not in task_status: return jsonify({"status": "invalid"}) return jsonify(task_status[task_id])
5. 加载页面(loading.html)前端逻辑
用JavaScript定时查询状态,完成后自动跳转:
<!DOCTYPE html> <html> <head> <title>数据抓取中</title> <script> const urlParams = new URLSearchParams(window.location.search); const taskId = urlParams.get('task_id'); // 每2秒查询一次任务状态 const checkInterval = setInterval(async () => { const response = await fetch(`/check-task-status?task_id=${taskId}`); const data = await response.json(); if (data.status === 'completed') { clearInterval(checkInterval); // 跳转到结果页面,可根据实际路由修改 window.location.href = `/result?user_id=${data.user_id}`; } }, 2000); </script> </head> <body> <h1>数据抓取中,请稍候...</h1> </body> </html>
方案二:WebSocket实时推送(实时性更强)
1. 初始化WebSocket相关存储
from quart import websocket # 存储WebSocket连接:key为task_id,value为websocket对象 active_connections = {}
2. 修改首页路由(同轮询方案步骤2)
@app.route('/', methods=['POST']) async def index(): form = await request.form user_id = int(form['user_id']) task_id = str(uuid4()) task_status[task_id] = {"status": "pending", "user_id": user_id} app.add_background_task(task_to_do, user_id=user_id, task_id=task_id) return redirect(f'/loading?task_id={task_id}')
3. 修改后台任务,完成后推送消息
async def task_to_do(user_id: int, task_id: str): await asyncio.sleep(60) task_status[task_id]["status"] = "completed" # 若存在对应WebSocket连接,推送完成信号 if task_id in active_connections: ws = active_connections[task_id] try: await ws.send_json({"status": "completed", "user_id": user_id}) except Exception: # 连接已关闭,移除无效记录 del active_connections[task_id]
4. 添加WebSocket路由
@app.websocket('/ws') async def ws(): task_id = request.args.get('task_id') if not task_id or task_id not in task_status: await websocket.close() return # 保存当前连接 active_connections[task_id] = websocket._get_current_object() try: # 保持连接存活 while True: await websocket.receive() except Exception: # 连接关闭时清理记录 if task_id in active_connections: del active_connections[task_id]
5. 加载页面的WebSocket逻辑
<!DOCTYPE html> <html> <head> <title>数据抓取中</title> <script> const urlParams = new URLSearchParams(window.location.search); const taskId = urlParams.get('task_id'); // 建立WebSocket连接 const ws = new WebSocket(`ws://${window.location.host}/ws?task_id=${taskId}`); ws.onmessage = function(event) { const data = JSON.parse(event.data); if (data.status === 'completed') { ws.close(); window.location.href = `/result?user_id=${data.user_id}`; } }; ws.onerror = function() { console.error('WebSocket连接出错'); }; </script> </head> <body> <h1>数据抓取中,请稍候...</h1> </body> </html>
注意事项
- 内存存储(
task_status、active_connections)仅适用于单进程部署,多进程或分布式场景请改用Redis等外部存储。 - 可定时清理长时间未处理的任务状态,避免内存泄漏。
内容的提问来源于stack exchange,提问作者BarleyZP
相关产品推荐
相关产品推荐

