Django APIView授权头获取与函数视图响应格式问题求助
问题描述
我有一个需认证才可获取数据的Django APIView,请求的用户令牌由React前端发送。使用函数视图时程序可运行,但返回数据格式不符合需求,会包含模型名称等冗余信息,格式类似[{"model":"product.product", "key":2, "fields":[{"name": "product one"} ...]}]。因此我改用APIView,但出现错误**"Given token not valid for any token type"**,移除认证部分后代码可正常运行,我认为问题出在APIView中授权头的获取方式。
我有两个问题:
- 使用APIView时获取请求头的正确方式是什么?
- 使用函数视图时,应选用哪种响应类来返回如
{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
相关产品推荐
相关产品推荐

