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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:06:19