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

Django APIView授权头获取与函数视图响应格式问题求助

问题描述

我有一个需认证才可获取数据的Django APIView,请求的用户令牌由React前端发送。使用函数视图时程序可运行,但返回数据格式不符合需求,会包含模型名称等冗余信息,格式类似[{"model":"product.product", "key":2, "fields":[{"name": "product one"} ...]}]。因此我改用APIView,但出现错误**"Given token not valid for any token type"**,移除认证部分后代码可正常运行,我认为问题出在APIView中授权头的获取方式。

我有两个问题:

  1. 使用APIView时获取请求头的正确方式是什么?
  2. 使用函数视图时,应选用哪种响应类来返回如{id: 1, name:"product one", price: 100}这类规范对象?我使用的是Google Firebase认证机制。

相关代码

APIView代码(报错版本)

class SingleProduct(APIView):
     def get(self, request, slug):
        authorization_header = request.META.get('HTTP_AUTHORIZATION')
        token = authorization_header.replace("Bearer ", "")
        try:
           decoded_token = auth.verify_id_token(token)
           product = Product.objects.get(slug=slug) 
        except Product.DoesNotExist:
            return Response({"error": "No product found"})
        serializer = ProductSerializer(product)
        return Response(serializer.data)

可运行的函数视图代码

from django.core.serializers import serialize
def single_product(request, slug):
    authorization_header = request.META.get('HTTP_AUTHORIZATION')
    token = authorization_header.replace("Bearer ", "")
    try:
        decoded_token = auth.verify_id_token(token)
        product = Product.objects.filter(slug=slug)
        result = {"data": serialize("json", product)}
    except: 
        return JsonResponse({"data":"No product found"})
    return JsonResponse(result)

前端请求头代码

const config = {
        method: "GET",
        headers: {
          Authorization: "Bearer " + token,
        },
      }; 

解决方案

问题1:APIView中获取令牌的正确方式

request.META.get('HTTP_AUTHORIZATION')本身是合法的写法,但报错核心原因大概率是未处理授权头为空/格式异常的情况,或是令牌验证时的异常未被捕获。另外DRF的request对象提供了更简洁的request.headers属性,可直接获取请求头。

修改后的APIView代码:

class SingleProduct(APIView):
    def get(self, request, slug):
        # 用request.headers简化写法,同时校验授权头合法性
        authorization_header = request.headers.get('Authorization')
        if not authorization_header or not authorization_header.startswith('Bearer '):
            return Response({"error": "无效或缺失的授权头"}, status=401)
        
        token = authorization_header.replace("Bearer ", "")
        try:
            decoded_token = auth.verify_id_token(token)
            product = Product.objects.get(slug=slug) 
        except Product.DoesNotExist:
            return Response({"error": "未找到产品"}, status=404)
        except auth.InvalidIdTokenError:
            # 精准捕获Firebase令牌验证失败异常
            return Response({"error": "无效令牌"}, status=401)
        
        serializer = ProductSerializer(product)
        return Response(serializer.data)

推荐进阶方案:封装Firebase认证类,避免重复代码

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class FirebaseAuthentication(BaseAuthentication):
    def authenticate(self, request):
        authorization_header = request.headers.get('Authorization')
        if not authorization_header or not authorization_header.startswith('Bearer '):
            return None  # 若需强制认证,可抛出AuthenticationFailed
        
        token = authorization_header.replace('Bearer ', '')
        try:
            decoded_token = auth.verify_id_token(token)
            # 可根据decoded_token关联Django用户,返回(user, token)
            return (None, decoded_token)  # 无需关联用户则返回(None, token)
        except auth.InvalidIdTokenError:
            raise AuthenticationFailed("Firebase令牌无效")

# 在APIView中使用
class SingleProduct(APIView):
    authentication_classes = [FirebaseAuthentication]
    
    def get(self, request, slug):
        # 认证通过后,request.auth可获取验证后的令牌信息
        try:
            product = Product.objects.get(slug=slug) 
        except Product.DoesNotExist:
            return Response({"error": "未找到产品"}, status=404)
        
        serializer = ProductSerializer(product)
        return Response(serializer.data)

问题2:函数视图返回规范格式对象

不要用django.core.serializers.serialize,它会生成带模型元数据的冗余格式。改用DRF的自定义序列化器+Response类即可生成规范结构。

修改后的函数视图代码:

from rest_framework.response import Response
from rest_framework.decorators import api_view
from .serializers import ProductSerializer  # 导入你的ProductSerializer

@api_view(['GET'])
def single_product(request, slug):
    authorization_header = request.headers.get('Authorization')
    if not authorization_header or not authorization_header.startswith('Bearer '):
        return Response({"error": "无效或缺失的授权头"}, status=401)
    
    token = authorization_header.replace("Bearer ", "")
    try:
        decoded_token = auth.verify_id_token(token)
        product = Product.objects.get(slug=slug)  # 用get获取单个对象,替代filter
        serializer = ProductSerializer(product)
        return Response({"data": serializer.data})
    except Product.DoesNotExist:
        return Response({"data": "未找到产品"}, status=404)
    except auth.InvalidIdTokenError:
        return Response({"error": "无效令牌"}, status=401)

关键改进:

  • 用@api_view让函数视图支持DRF的响应处理
  • 用自定义序列化器生成{id:1, name:"product one", ...}格式数据
  • 替换filter为get,适配单个产品的查询场景
  • 使用Response替代JsonResponse,自动处理内容类型和状态码

内容的提问来源于stack exchange,提问作者Mart

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:53:09