使用Fetch API发送POST请求到Django后端无法正常工作
问题原因
两个报错是连锁触发的,核心问题是前后端数据格式不匹配:
- 原生表单POST提交时默认使用
application/x-www-form-urlencoded编码,Django会自动把请求参数解析到request.POST字典中,所以原生表单提交时逻辑正常 - 你写的Fetch请求指定了
Content-Type: application/json,参数以JSON格式放在请求体中,Django不会把这部分数据解析到request.POST里,request.POST.get('id')取到的是None,执行Product.objects.get(id=None)自然触发DoesNotExist异常 - 服务端抛出500错误后返回的是默认HTML错误页(页面内容以
<开头),前端代码尝试把这段HTML当JSON解析,就触发了Unexpected token < in JSON at position 0的语法错误
另外你的代码还有4个隐藏bug:
- 按钮传入的商品id是2,Fetch请求里硬编码写死了id为'1',就算格式对也加错商品
- 前端响应处理逻辑错误:
res.json()本身就返回解析后的JSON对象Promise,不需要再套一层JSON.stringify - Django的
JsonResponse直接传字符串会触发类型错误,默认只接受字典类型参数,传非字典值需要加safe=False - 没带Django默认要求的CSRF令牌,修复格式问题后大概率会触发403权限错误
修复方案
1. 后端视图修复
兼容JSON和表单两种提交方式,加上必要的参数校验,修正JsonResponse写法:
import json from django.http import JsonResponse from .models import Product # 保留你原有的Cart类导入 def cart_add(request): if request.method == 'POST': cart = Cart(request) # 区分请求格式取参数 if request.content_type == "application/json": request_data = json.loads(request.body) product_id = request_data.get('id') else: product_id = request.POST.get('id') # 空参数提前返回,避免查库报错 if not product_id: return JsonResponse({"code": 400, "msg": "缺少商品ID"}, status=400) product = Product.objects.get(id=product_id) cart.add(product=product) return JsonResponse({"code": 200, "msg": "添加成功"})
2. 前端Fetch逻辑修复
修正参数传递、响应解析逻辑,加上CSRF令牌,路径尽量写绝对路径避免相对路径跳转错误:
// 工具方法:从cookie中取Django自动生成的CSRF令牌 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; } const csrftoken = getCookie('csrftoken'); function GFG_click(clicked) { // 路径替换成你自己的实际接口绝对路径,避免相对路径出错 fetch('/playground/cart/add/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken }, body: JSON.stringify({ id : clicked // 使用按钮传入的id,不要硬编码 }), }) .then(res => { if (!res.ok) throw new Error('请求失败'); return res.json(); }) .then(data => { console.log('操作成功', data); // 在这里写添加成功后的逻辑,比如更新购物车角标、弹出提示等 }) .catch(err => console.log(err)); }
可选简化方案
如果你不想改后端解析JSON的逻辑,直接把Fetch的提交格式改成和原生form一致的表单格式就行,代码更简单,不需要额外解析JSON:
function GFG_click(clicked) { const formData = new FormData(); formData.append('id', clicked); fetch('/playground/cart/add/', { method: 'POST', headers: { 'X-CSRFToken': csrftoken }, body: formData }) .then(res => res.json()) .then(data => { // 后续业务逻辑 }) .catch(err => console.log(err)); }
这种写法不需要手动指定Content-Type,浏览器会自动设置为multipart/form-data,Django可以正常从request.POST里取到参数,后端原有代码不用大改也能跑。
内容的提问来源于stack exchange,提问作者Reza Namvar
相关产品推荐
相关产品推荐

