You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRF批量请求端点认证异常:CSRF未设置问题排查

解决DRF批量请求中认证与CSRF错误的问题

你的核心问题在于直接复制原始WSGI请求(request._request)时,没有传递DRF已经处理好的认证上下文,同时触发了不必要的CSRF检查。单独调用接口时,DRF的中间件会完整处理Token认证和请求上下文,但批量调用时,你复制的原始请求丢失了这些关键信息,导致子请求重新走认证流程时失败。

问题根源拆解

  1. DRF请求上下文丢失:你复制的request._request是底层的WSGI请求对象,而DRF的request(外层包装对象)才包含经过认证的user、auth等属性。直接复制原始请求,这些认证信息不会被传递给子视图。
  2. CSRF验证误触发:因为你同时启用了SessionAuthentication,当子请求是POST方法时,Django会自动检查CSRF cookie,但批量请求的上下文里并没有这个cookie,所以返回403错误。而单独调用时,TokenAuthentication会豁免CSRF检查(因为API请求通常不用Session)。

修复后的代码示例

下面是调整后的批量视图代码,解决了认证传递和CSRF问题,同时处理了POST数据的传递:

from copy import copy
from rest_framework.request import Request
from django.urls import resolve
from rest_framework.response import Response
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework.permissions import IsAuthenticated
import json

@api_view(["POST"])
@authentication_classes((SessionAuthentication, TokenAuthentication))
@permission_classes((IsAuthenticated,))
def batch(request):
    operations = request.data
    for operation in operations:
        # 复制原始WSGI请求,并用DRF的Request包装以保留上下文
        wsgi_request = copy(request._request)
        op_request = Request(wsgi_request)
        
        # 直接传递已认证的用户和Token信息,避免子请求重复认证
        op_request.user = request.user
        op_request.auth = request.auth
        
        # 跳过CSRF检查:因为我们用Token认证,批量请求属于API调用,无需CSRF
        wsgi_request.csrf_processing_done = True
        
        # 设置子请求的方法和路径
        op_request.method = operation["method"]
        op_request.path = operation["path"]
        
        # 处理POST/PUT/PATCH请求的数据传递
        if operation.get("data") and op_request.method in ["POST", "PUT", "PATCH"]:
            op_request.data = operation["data"]
            # 更新WSGI请求的body和Content-Type,确保视图能正确读取数据
            wsgi_request.body = json.dumps(operation["data"]).encode('utf-8')
            wsgi_request.META['CONTENT_TYPE'] = 'application/json'
        
        # 解析并调用目标视图
        view, args, kwargs = resolve(operation["path"])
        response = view(op_request, *args, **kwargs)
        
        # 收集响应结果
        operation["response"] = {
            "status": response.status_code,
            "data": response.data
        }
    
    return Response(operations)

关键修改说明

  • 用DRF Request包装:通过Request(wsgi_request)把原始请求包装成DRF的请求对象,确保能使用DRF的请求上下文。
  • 直接传递认证信息:把原请求已经认证好的user和auth赋值给子请求,避免每个子请求都重新解析Token,同时保证认证状态正确。
  • 跳过CSRF检查:设置csrf_processing_done = True,告诉Django已经处理过CSRF验证,避免SessionAuthentication触发不必要的检查。
  • 处理请求数据:手动把operation["data"]传递给子请求,同时更新WSGI请求的body和Content-Type,确保POST/PUT等方法能正确获取到参数。

额外优化建议

如果你的API只需要Token认证,建议把authentication_classes改成仅包含TokenAuthentication,这样从根源上避免CSRF相关的问题:

@authentication_classes((TokenAuthentication,))

内容的提问来源于stack exchange,提问作者user4052054

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:16:55