Django 5 REST Framework:如何在API请求处理前返回加载提示?
解决方案
HTTP协议本身是单次请求对应单次响应,没法在同一个请求里先返回加载提示,再返回最终处理结果。但可以通过以下两种方式实现类似需求:
方案一:前端配合后端异步任务(推荐)
这是最符合HTTP规范、用户体验最好的方式,核心流程如下:
- 前端发起请求后,后端立即返回一个任务ID,前端同步显示"Loading... Please wait."
- 后端在后台异步执行耗时任务
- 前端通过轮询或WebSocket,用任务ID查询任务状态,直到任务完成后获取最终结果
具体实现(基于Celery异步任务)
1. 基础Celery配置(以Redis为消息中间件)
# project/celery.py import os from celery import Celery os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') app = Celery('project') app.config_from_object('django.conf:settings', namespace='CELERY') app.autodiscover_tasks()
2. 定义耗时任务
# app/tasks.py from celery import shared_task import time @shared_task def long_running_task(): # 模拟耗时操作,替换为实际业务逻辑 time.sleep(10) return {"result": "耗时任务处理完成"}
3. 修改API视图
from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .tasks import long_running_task class SomeView(APIView): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsAuthenticated] def get(self, request): # 启动异步任务 task = long_running_task.delay() # 返回任务ID,供前端查询状态 return Response({"task_id": task.id}, status=status.HTTP_202_ACCEPTED) # 新增任务状态查询视图 class TaskStatusView(APIView): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsAuthenticated] def get(self, request, task_id): from celery.result import AsyncResult task = AsyncResult(task_id) if task.state == 'PENDING': response = {"state": task.state, "status": "任务正在处理中..."} elif task.state == 'SUCCESS': response = {"state": task.state, "result": task.result} else: response = {"state": task.state, "status": f"任务异常: {task.info}"} return Response(response)
4. 前端示例(JavaScript)
async function fetchData() { const loadingElement = document.getElementById('loading'); loadingElement.textContent = 'Loading... Please wait.'; // 发起任务请求 const taskResponse = await fetch('/api/some-view/', { method: 'GET', credentials: 'include' }); const taskData = await taskResponse.json(); const taskId = taskData.task_id; // 轮询任务状态 const pollInterval = setInterval(async () => { const statusResponse = await fetch(`/api/task-status/${taskId}/`, { method: 'GET', credentials: 'include' }); const statusData = await statusResponse.json(); if (statusData.state === 'SUCCESS') { clearInterval(pollInterval); loadingElement.textContent = ''; // 展示最终结果 document.getElementById('result').textContent = JSON.stringify(statusData.result); } else if (statusData.state !== 'PENDING') { clearInterval(pollInterval); loadingElement.textContent = statusData.status; } }, 1000); }
方案二:流式响应(不推荐,兼容性差)
利用Django的StreamingHttpResponse分块返回内容,先输出加载提示,再执行耗时任务后输出结果。但这种方式在多数浏览器中会等到全部内容加载完成后才显示,体验不佳,且不符合REST API的设计规范。
代码示例
from django.http import StreamingHttpResponse from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView import time class SomeView(APIView): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsAuthenticated] def get(self, request): def stream_response(): # 先输出加载提示 yield "Loading... Please wait.\n" # 执行耗时任务 time.sleep(10) # 输出最终结果 yield '{"result": "耗时任务处理完成"}' return StreamingHttpResponse(stream_response(), content_type='text/plain')
内容的提问来源于stack exchange,提问作者Coderio
相关产品推荐
相关产品推荐

