代码之家  ›  专栏  ›  技术社区  ›  Fcmam5

在使用DRF和django revproxy时获得“异常:读取请求的数据流后无法访问body”

  •  0
  • Fcmam5  · 技术社区  · 7 年前

    django-revproxy Django REST Framework 在我的项目中。我公开了一个API,用户可以从中获得有关聊天机器人的分析信息,其工作原理如下:

    • 用户从Django项目发送分析请求
    • if True 它联系另一个外部服务。

    我的 :

    # urls.py
    urlpatterns = (
        url(r'^analytics/(?P<path>.*)$', api.AnalyticsFunctionsProxyView.as_view()),
    )
    

    在我看来:

    # views.py
    from rest_framework.authentication import TokenAuthentication
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework.permissions import IsAuthenticated
    from revproxy.views import ProxyView
    from .permissions import HasChatBotPermission
    
    ...
    
    class AnalyticsFunctionsProxyView(ProxyView):
        upstream = settings.ANALYTICS_FAAS_URL
    
        def parse_body(self, request):
            if isinstance(request, rest_framework.request.Request):
                return request.data
            return super(AnalyticsFunctionsProxyView, self).parse_body(request)
    
        @classmethod
        def as_view(cls, *args, **kwargs):
            view = super(AnalyticsFunctionsProxyView, cls).as_view(*args, **kwargs)
            view = permission_classes((IsAuthenticated, HasChatBotPermission,))(view)
            view = authentication_classes([TokenAuthentication, JSONWebTokenAuthentication])(view)
            view = api_view(['GET', 'POST'])(view)
    
    
     return view
    

    HasChatBotPermission 权限

    #permissions.py
    class HasChatBotPermission(permissions.BasePermission):
        def has_permission(self, request, view):
            try:
                bot_name = request.data.get('name')
                user = request.user
                self.message = 'Permission denied for {}'.format(name)
                return ChatBot.objects.filter(user=user, project_name=project_id).exists()
            except Exception:
                self.message = 'Permission denied, no project_id was defined!'
                return False
    

    调用视图时会引发此异常:

    Traceback (most recent call last):
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/request.py", line 379, in __getattribute__
        return super(Request, self).__getattribute__(attr)
    AttributeError: 'Request' object has no attribute 'body'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 42, in inner
        response = get_response(request)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
        response = self.process_exception_by_middleware(e, request)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
        response = wrapped_callback(request, *callback_args, **callback_kwargs)
      File "/home/fcmam5/anaconda3/lib/python3.6/contextlib.py", line 52, in inner
        return func(*args, **kwds)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
        return view_func(*args, **kwargs)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
        return self.dispatch(request, *args, **kwargs)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/views.py", line 477, in dispatch
        response = self.handle_exception(exc)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/views.py", line 437, in handle_exception
        self.raise_uncaught_exception(exc)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/views.py", line 474, in dispatch
        response = handler(request, *args, **kwargs)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/decorators.py", line 52, in handler
        return func(*args, **kwargs)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
        return self.dispatch(request, *args, **kwargs)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/revproxy/views.py", line 204, in dispatch
        proxy_response = self._created_proxy_response(request, path)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/revproxy/views.py", line 139, in _created_proxy_response
        request_payload = request.body
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/request.py", line 383, in __getattribute__
        return getattr(self._request, attr)
      File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/http/request.py", line 264, in body
        raise RawPostDataException("You cannot access body after reading from request's data stream")
    django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
    

    问题是由这条线引起的 bot_name = request.data.get('name') 在我的 permissions.py ,当我直接传递字符串时,它会毫无问题地传递。

    我的问题是:

    • 如何访问请求主体而不发生此错误?为什么我会犯这个错误?
    • 使用Django revproxy检查用户权限是否有更好的解决方案。

    这是我在Stackoverflow的第一个问题,如果我的问题不清楚,我很抱歉,而且我的英语很差:)

    1 回复  |  直到 7 年前
        1
  •  1
  •   AKX Bryan Oakley    7 年前

    django-revproxy

    然而,使用Django(和WSGI以及缓冲)语义,一旦您以任何方式访问了请求体,就不可能实现这一点 一条原始的小溪,当你 request.data.get('name') . 这会根据DRF的请求协商配置将请求主体解析为JSON、HTTP multipart等等,并使用流。

    • 通过 bot_name 身体以外的地方;例如,查询字符串参数、HTTP头、URL的一部分,这样就不需要访问主体,或者
    • 自己使用 requests 而不是反向代理(这基本上是相同的事情,但增加了魔法,可以尝试按原样复制请求)。