Django rest framework自定义响应中间件

3

我使用Django Rest Framework和rest_auth(/login,/logout/,/register...)进行开发。

我有一个中间件,通常由user_logged_in或user_logged_out信号触发。

在我的中间件中,我想使用Rest框架的Response对象。

我的middleware.py文件如下:

from django.contrib.sessions.models import Session
from django.contrib.auth import logout
from django.contrib.auth.models import AnonymousUser
from rest_framework.response import Response
from rest_framework import status


class OneSessionPerUserMiddleware:
    def __init__(self, get_response):

        self.get_response = get_response

    def __call__(self, request):

        if request.user.is_authenticated:
            if request.user.session_key != Session.objects.filter(session_key__in = request.user.session_key):
                logout(request)
                return Response(request)
        return self.get_response(request)

我想我已经满足了条件,但是我收到了这个错误:

 The response content must be rendered before it can be accessed

如何在中间件中正确使用API响应对象?
我真的不明白self.get_response = get_response是什么意思?


从英文翻译成中文的编程相关内容。只返回翻译后的文本:只需删除return Response(request)这一行代码。让您的视图使用return self.get_response(request)来处理请求。无论如何,您都应该优雅地处理已注销的情况。 - dirkgroten
那么,我不能使用默认的API显示来添加一些自定义消息,比如“已登录”吗? - Isador
如果你真的想在中间件中处理所有请求,无论调用的是哪个API视图,那么请参考Nafees给出的答案。 - dirkgroten
2个回答

9
return Response(request) 这不是django可以处理的。你正在将request对象传递给Response,这意味着什么?
实际上,rest框架中的RequestResponse类与Django不兼容。rest框架在APIView中使用Request对象包装Django内置的WSGIRequest,并从视图获取Response对象后,通过取消包装创建HttpResponse对象。因此,Response对象只能在rest框架视图中使用。您可以像这样在中间件中使用来自django.httpJsonResponse
from django.http import JsonResponse


if request.user.is_authenticated:
    if request.user.session_key != Session.objects.filter(session_key__in = request.user.session_key):
        logout(request)
        return JsonResponse({'logout': True}) # or whatever data you want to return 
return self.get_response(request)

3
首先:如果您使用的身份验证类不是rest_framework.authentication.SessionAuthentication,那么request.user是在中间件之外设置的(可能在View.dispatch期间的某个地方)。
如果您确定request.user始终对应于rest_framework视图内的request.user,并且只想返回响应:
A. 查看APIView.finalize_response:它相对来说比较“复杂”,因为它可以使用不同的渲染器(取决于accept-content请求头),它可能会更改标头。如果您可以在中间件中访问视图实例,则可以调用view.finalize_response(request, response),如果没有,则可以尝试执行rest_framework.views.APIView().finalize_response(...)
B. 你可以使用 django.http.HttpResponse:手动生成字符串作为正文,并指定适当的 content_type。而且,django.http.JsonResponse 也可能很方便。
C. 我假设你实际上不是在使用 return Response(request),而是在做其他事情(比如 Response(data=...))。如果你只需要从视图返回结果,则删除 return Response(request),这样 return self.get_response(request) 就会启动。但是你正在进行注销操作,因此也许应该返回 django.http.HttpResponseRedirect 的某个实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接