Django REST Framework 中的 HTTP 204 响应陷阱

问题现象

删除接口调用成功,但前端读取不到响应体中的 message 或 result,于是把成功操作显示成失败。

根本原因

DRF 的 ModelViewSet 删除操作默认返回 204 No Content。按照 HTTP 语义,204 响应不包含响应体;浏览器的 fetch、Axios 等客户端不会提供可供业务代码读取的响应体。

Postman 等调试工具有时会展示服务端实际发出的原始字节,但这不代表 204 响应可以携带业务响应体,也不应依赖这种行为。

renderer_classes = [JSONRenderer] 本身不是问题原因。关键在于 DRF 的删除操作返回了 204,而调用方却期待 JSON 响应体。

方案一:保留 204,前端检查状态码

如果接口采用标准的无内容删除响应,前端应根据 HTTP 状态判断成功,而不是检查响应体:

const response = await axios.delete(`/api/books/${id}/`)
if (response.status === 204) {
  // 删除成功
}

方案二:需要返回 JSON 时使用 200

如果 API 合约要求返回消息或业务字段,可以重写 destroy 并返回 200:

from rest_framework import status, viewsets
from rest_framework.response import Response

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(
            {"message": "删除成功", "result": True},
            status=status.HTTP_200_OK,
        )

经验总结

  • 选择 204 时,接口不应返回响应体,客户端应检查状态码。
  • 如果客户端必须读取 JSON,就使用 200 等允许包含响应体的状态码,并保持前后端接口合约一致。
  • 用 Postman 测试后,还应使用实际浏览器客户端验证行为。