簡體   English   中英

使用DRF和django-revproxy時出現“異常:從請求的數據流讀取后無法訪問正文”

[英]Getting “Exception: You cannot access body after reading from request's data stream” while working with DRF and django-revproxy

我在項目中使用django-revproxyDjango REST Framework 我公開了一個API,用戶可以在其中獲取有關其聊天機器人的分析,其工作原理如下:

  • 用戶發送來自Django項目的分析請求
  • Django項目,檢查用戶是否已通過身份驗證並擁有該聊天機器人
  • if True它將與另一個外部服務聯系。

我的urls.py

# 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

問題是由我的permissions.py的這一行bot_name = request.data.get('name')引起的,當我直接傳遞字符串時,它傳遞沒有任何問題。

我的問題是:

  • 如何在沒有此錯誤的情況下訪問請求正文? 為什么我出現此錯誤?
  • 有沒有更好的解決方案,可以使用Django revproxy檢查用戶權限。

這是我在Stackoverflow中的第一個問題,對不起,如果我的問題不是英語,並且對我的英語不好:)

您遇到此錯誤是因為django-revproxy嘗試讀取原始請求正文,以便它可以創建到上游服務器的代理請求。

然而,Django的(和WSGI的,和緩沖)的語義,一旦你訪問請求主體, 只能作為原料流,當你做到這一點是不可能的request.data.get('name') 根據DRF的請求協商配置,此操作將請求正文解析為JSON,HTTP多部分(無論如何),並使用流。

據我所知,有兩種解決方法:

  • bot_name傳遞到身體以外的其他地方; 例如查詢字符串參數,HTTP標頭,URL的一部分,因此您不需要訪問正文,或者
  • 使自己使用requests而不是反向代理來進行后端請求(這基本上是同一件事,但是增加了嘗試按原樣復制請求的功能)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM