React/Django项目中某路由CSRF令牌错误,另一路由正常
解决Django普通View CSRF验证失败问题
这个问题我之前也碰到过,核心原因是Django普通View和DRF APIView在CSRF验证逻辑上的差异!让我给你拆解一下:
问题根源
Django普通View(你的
RejectedDocumentsView)默认依赖Django的CSRF中间件,它要求请求同时满足两个条件:- 请求Cookie中包含
csrftoken - 要么POST表单数据里带
csrfmiddlewaretoken字段,要么请求头里带X-CSRFToken
你的rejectedDocuments()只满足了第一个条件,所以在到达视图前就被CSRF中间件拦截报错。
- 请求Cookie中包含
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
相关产品推荐
相关产品推荐

