如何解决JS Fetch向Django REST API发起POST请求的400错误
问题原因
报错的核心是请求头的Content-Type与请求体格式不匹配:
你在fetch请求里手动设置了Content-Type: application/json,但请求体传入的是FormData对象——FormData默认使用multipart/form-data编码格式,而非JSON。服务器收到这种混合格式的请求后,尝试按JSON解析就会失败,抛出"JSON parse error"。
另外还有一个潜在问题:BookingForm包含client字段,但前端表单大概率没有传递这个值(正常逻辑下应该自动关联当前登录用户),即使解决了JSON问题,后续也可能因缺少client字段导致请求失败。
解决方案
方案1:将FormData转为JSON格式发送
修改JS代码,把FormData转换为普通对象后序列化为JSON字符串,保持Content-Type为application/json:
csrf = document.getElementsByName("csrfmiddlewaretoken")[0].value; const formdata = new FormData(form); // 将FormData转为普通对象 const dataObj = {}; formdata.forEach((value, key) => { dataObj[key] = value; }); fetch('/api/booking/', { method: 'POST', headers: { "X-CSRF-Token": csrf, "Content-Type": "application/json" }, credentials: "same-origin", body: JSON.stringify(dataObj) }) .then(res => res.json()) .then(data => console.log(data));
方案2:使用FormData默认的multipart/form-data格式
去掉手动设置的Content-Type,fetch会自动为FormData设置正确的multipart/form-data请求头(包含boundary参数),DRF序列化器可直接解析这种格式:
csrf = document.getElementsByName("csrfmiddlewaretoken")[0].value; const formdata = new FormData(form); fetch('/api/booking/', { method: 'POST', headers: { "X-CSRF-Token": csrf, // 移除Content-Type设置,让fetch自动处理 }, credentials: "same-origin", body: formdata }) .then(res => res.json()) .then(data => console.log(data));
处理client字段的潜在问题
client是关联User的外键,不应该让前端传递(避免篡改),建议在后端视图自动设置当前登录用户:
修改BookingView类,添加perform_create方法:
from rest_framework.permissions import IsAuthenticated class BookingView(generics.ListCreateAPIView): queryset = Booking.objects.all() serializer_class = BookingSerializer # 限制仅登录用户可创建预订 permission_classes = [IsAuthenticated] def perform_create(self, serializer): # 将当前登录用户设为预订的client serializer.save(client=self.request.user)
同时修改BookingForm和序列化器,移除前端不需要传递的client字段:
# forms.py class BookingForm(ModelForm): class Meta: model = Booking fields = ['date', 'hour'] # 移除client字段 # 对应的BookingSerializer示例 class BookingSerializer(serializers.ModelSerializer): class Meta: model = Booking fields = ['id', 'date', 'hour', 'client'] # 返回时可保留client,创建时无需前端传入
内容的提问来源于stack exchange,提问作者Boris Marchand
相关产品推荐
相关产品推荐

