Django DRF自定义认证中间件出现accepted_renderer未设置错误如何修复
根因分析
你遇到的AssertionError: .accepted_renderer not set on Response错误核心原因是:DRF的Response对象依赖DRF视图内部的内容协商流程来设置accepted_renderer等必要属性,你在Django原生中间件的process_view阶段直接返回DRF Response,此时还没有进入@api_view/APIView的处理逻辑,Response缺少必要属性,无法正常序列化返回。
修复方案
这里提供两种可行的修复方式,可根据需求选择:
方案1:中间件返回Django原生JsonResponse(改动最小)
直接把中间件里所有返回Response的地方替换为Django原生的JsonResponse,原生响应不需要经过DRF的渲染流程,可直接在中间件层返回。
修改后的中间件代码:
from datetime import datetime from django.http import JsonResponse # 新增导入 from rest_framework import status from cheers.core.api.jwt_helpers import decode_cognito_jwt class CognitoMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): return self.get_response(request) def process_view(self, request, view_func, view_args, view_kwargs): auth = request.headers.get("Authorization", None) if not auth: return JsonResponse(dict(error='Authorization header expected'), status=status.HTTP_401_UNAUTHORIZED) parts = auth.split() if parts[0].lower() != "bearer": return JsonResponse(dict(error='Authorization header must start with bearer'), status=status.HTTP_401_UNAUTHORIZED) elif len(parts) == 1: return JsonResponse(dict(error='Token not found'), status=status.HTTP_401_UNAUTHORIZED) elif len(parts) > 2: return JsonResponse(dict(error='Authorization header must be Bearer token'), status=status.HTTP_401_UNAUTHORIZED) token = parts[1] try: res = decode_cognito_jwt(token) expiration = datetime.utcfromtimestamp(res['exp']) current_utc = datetime.utcnow() if current_utc > expiration: return JsonResponse(dict(error=f'current time:{current_utc} is after expiration:{expiration}', user_msg='Please login again'), status=status.HTTP_400_BAD_REQUEST) except Exception: # Fail if invalid return JsonResponse(dict(error="Invalid JWT"), status=status.HTTP_401_UNAUTHORIZED) else: # 认证通过,继续进入视图处理 return None
该方案不需要调整其他代码,直接替换返回类型即可兼容所有视图。
方案2:改为DRF全局认证类(更符合DRF规范)
如果希望统一走DRF的认证流程,更推荐把认证逻辑封装为DRF的认证类,然后全局配置,天然兼容APIView和@api_view装饰的视图:
- 新建认证类:
from datetime import datetime from rest_framework import authentication, exceptions, status from cheers.core.api.jwt_helpers import decode_cognito_jwt class CognitoAuthentication(authentication.BaseAuthentication): def authenticate(self, request): auth = request.headers.get("Authorization", None) if not auth: raise exceptions.AuthenticationFailed('Authorization header expected') parts = auth.split() if parts[0].lower() != "bearer": raise exceptions.AuthenticationFailed('Authorization header must start with bearer') elif len(parts) == 1: raise exceptions.AuthenticationFailed('Token not found') elif len(parts) > 2: raise exceptions.AuthenticationFailed('Authorization header must be Bearer token') token = parts[1] try: res = decode_cognito_jwt(token) expiration = datetime.utcfromtimestamp(res['exp']) current_utc = datetime.utcnow() if current_utc > expiration: raise exceptions.AuthenticationFailed(f'current time:{current_utc} is after expiration:{expiration}, Please login again') except Exception: raise exceptions.AuthenticationFailed("Invalid JWT") # 认证通过返回用户和token,可根据实际需求构造用户对象 return (None, res)
- 在settings.py中配置全局DRF认证,同时删除原来的中间件配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'cheers.core.api.authentication.CognitoAuthentication', # 替换为实际的认证类路径 ] } # 移除MIDDLEWARE里的CognitoMiddleware配置 MIDDLEWARE = [ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ]
该方案的优势是完全贴合DRF的设计规范,错误响应会自动走DRF的渲染流程,和其他DRF接口的错误格式保持统一。
内容的提问来源于stack exchange,提问作者user12314098
相关产品推荐
相关产品推荐

