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

React/Django项目中某路由CSRF令牌错误,另一路由正常

解决Django普通View CSRF验证失败问题

这个问题我之前也碰到过,核心原因是Django普通View和DRF APIView在CSRF验证逻辑上的差异!让我给你拆解一下:

问题根源

  • Django普通View(你的RejectedDocumentsView)默认依赖Django的CSRF中间件,它要求请求同时满足两个条件:

    1. 请求Cookie中包含csrftoken
    2. 要么POST表单数据里带csrfmiddlewaretoken字段,要么请求头里带X-CSRFToken
      你的rejectedDocuments()只满足了第一个条件,所以在到达视图前就被CSRF中间件拦截报错。
  • DRF APIView(你的SaveDocumentAPIView)由Django REST Framework处理认证逻辑:如果使用默认的SessionAuthentication,DRF会自动从Cookie中提取CSRF Token并完成验证;另外你上传文件的submitDocuments()可能通过表单提交自动携带了csrfmiddlewaretoken字段,所以请求能正常通过。

解决方案(按推荐优先级排序)

方案1:给rejectedDocuments()请求添加X-CSRFToken头

这是最安全且符合Django规范的做法,手动在请求头中带上CSRF Token:

// 先写一个从Cookie中提取CSRF Token的工具函数
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

// 修改你的rejectedDocuments函数
function rejectedDocuments() {
    const csrftoken = getCookie('csrftoken');
    const rejectedFiles = ['不符合扩展名的文件1', '不符合扩展名的文件2']; // 你的文件名数组

    fetch('/api/84/documents/upload/error', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': csrftoken, // 关键:添加这个请求头
        },
        body: JSON.stringify({ rejectedDocuments: rejectedFiles }),
        credentials: 'include' // 确保发送Cookie到后端
    })
    .then(response => {
        if (!response.ok) throw new Error('请求失败');
        return response.json();
    })
    .then(data => console.log('通知发送成功:', data))
    .catch(error => console.error('错误:', error));
}

方案2:将RejectedDocumentsView改为DRF APIView

既然你的项目已经在用DRF,把这个视图统一改成APIView可以保持认证逻辑一致性,避免重复处理CSRF:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
# 导入你需要的其他模块(比如邮件发送)

class RejectedDocumentsView(APIView):
    def post(self, request, *args, **kwargs):
        # 从请求中获取前端发送的拒绝文件列表
        rejected_files = request.data.get('rejectedDocuments', [])
        
        # 执行你的邮件通知逻辑
        # send_admin_notification(rejected_files)
        
        return Response({
            'status': 'success',
            'message': f'已通知管理员审核{len(rejected_files)}个文件'
        }, status=status.HTTP_200_OK)

之后记得更新路由配置,确保指向这个新的APIView。

方案3:给普通View添加CSRF豁免(不推荐)

如果这个接口是完全公开的、无敏感操作的,可以临时用csrf_exempt跳过验证,但这会带来安全风险,谨慎使用:

from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json

@csrf_exempt
def rejected_documents_view(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        rejected_files = data.get('rejectedDocuments', [])
        
        # 邮件通知逻辑
        
        return JsonResponse({'status': 'success'})
    return JsonResponse({'status': 'error'}, status=405)

总结

优先选方案1或方案2,既能保证安全性,又能和现有项目的技术栈保持一致,避免后续出现类似的认证问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:37:23