Django中间件报错ContentNotRenderedError:讲师账号自动锁定功能异常
问题描述
我需要实现以下功能:
- 管理员为讲师创建账号,默认密码为
ou@123 - 若讲师在设定时间(测试设为1分钟)内未修改密码,系统自动锁定账号(设置
is_active=False)
我在middlewares和views中编写了处理代码,但运行时出现错误:
django.template.response.ContentNotRenderedError: The response content must be rendered before it can be accessed.
代码片段
middlewares.py
class PasswordChangeMiddleware(MiddlewareMixin): def process_request(self, request): if request.user.is_authenticated and request.user.is_staff: if not request.user.password_changed: if timezone.now() > request.user.date_joined + timedelta(minutes=1): request.user.is_active = False request.user.save() return None def process_response(self, request, response): if request.user.is_authenticated and not request.user.is_active: return Response({"message": "Account Locked"}, status=status.HTTP_403_FORBIDDEN) return response
settings.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'alumni.middlewares.PasswordChangeMiddleware', ]
views.py
class UserViewSet(viewsets.ViewSet, generics.CreateAPIView): queryset = User.objects.filter(is_active=True) serializer_class = serializers.UserSerializer parser_classes = [parsers.MultiPartParser, ] def get_permissions(self): if self.action in ['current_user', 'list_posts', 'get_posts', 'my_post', 'change_password']: return [permissions.IsAuthenticated()] elif self.action in ['create_lecturer']: return [permissions.IsAdminUser()] return [permissions.AllowAny()] @action(methods=['get', 'patch'], url_path='current-user', detail=False) def current_user(self, request): user = request.user if request.method.__eq__('PATCH'): for k, v in request.data.items(): setattr(user, k, v) # user.k = v user.save() return Response(serializers.UserSerializer(user).data) @action(methods=['post'], url_path='change-password', detail=False) def change_password(self, request): user = request.user current_password = request.data.get('current_password') new_password = request.data.get('new_password') confirm_password = request.data.get('confirm_password') if user.check_password(current_password): if new_password == confirm_password: user.set_password(new_password) user.password_changed = True user.save() return Response({"success": "Mật khẩu đã được thay đổi thành công!"}) else: return Response({"error": "Xác nhận mật khẩu không khớp!"}, status=status.HTTP_400_BAD_REQUEST) return Response({"error": "Mật khẩu hiện tại không chính xác!"}, status=status.HTTP_400_BAD_REQUEST) @action(methods=['post'], url_path='create-lecturer', detail=False) def create_lecturer(self, request): data = request.data.copy() data['password'] = make_password('ou@123') data['is_staff'] = True serializer = self.serializer_class(data=data) if serializer.is_valid(): user = serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
解决方案
错误原因
Django中间件的process_response方法要求返回Django原生的HttpResponse对象,但你返回了DRF的Response对象。DRF的Response需要经过DRF渲染器处理才能生成可访问的内容,直接在中间件返回会触发ContentNotRenderedError。
修改步骤
替换DRF Response为Django JsonResponse
修改middlewares.py的process_response方法,导入JsonResponse并替换返回语句:from django.http import JsonResponse class PasswordChangeMiddleware(MiddlewareMixin): # 其他代码保持不变 def process_response(self, request, response): if request.user.is_authenticated and not request.user.is_active: return JsonResponse({"message": "Account Locked"}, status=403) return response优化账号锁定逻辑
当前process_request中每次请求都会检查时间并更新用户状态,可能存在性能问题。可以改为:- 仅当用户尝试访问需要认证的接口时才触发检查
- 或者使用Django
user_logged_in信号,在用户登录时检查锁定状态 - 更高效的方式是用定时任务(如Celery)批量处理过期未改密码的账号
处理已锁定用户的session
账号被锁定后,需要清除用户session,避免用户通过旧session继续访问:def process_request(self, request): if request.user.is_authenticated and request.user.is_staff: if not request.user.password_changed: if timezone.now() > request.user.date_joined + timedelta(minutes=1): request.user.is_active = False request.user.save() # 清除当前session request.session.flush() return None
内容的提问来源于stack exchange,提问作者Trần Lê Lân
相关产品推荐
相关产品推荐

