Получение «Исключение: вы не можете получить доступ к телу после чтения из потока данных запроса» при работе с DRF и django-revproxy - PullRequest
0 голосов
/ 08 октября 2018

Я использую django-revproxy и Django REST Framework в моем проекте.И я раскрываю API, где пользователи получают аналитику о своих чат-ботах, и он работает следующим образом:

  • Пользователь отправляет запросы на аналитику из проекта Django
  • проекта Django,проверяет, прошел ли пользователь проверку подлинности и владеет ли он этим чатботом
  • if True, он связывается с другой внешней службой.

My 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

Проблема вызвана этой строкой bot_name = request.data.get('name') в моем permissions.py, когда я передаю строку напрямую, она проходит без проблем.

Мой вопрос:

  • Как я могу получить доступ к телу запроса без этой ошибки?Почему у меня появляется эта ошибка?
  • Есть ли лучшее решение для проверки прав пользователя с помощью Django revproxy.

Это мой первый вопрос в Stackoverflow, извините, еслиу меня вопрос не кля, а для моего плохого английского :) 1046 *

1 Ответ

0 голосов
/ 08 октября 2018

Эта ошибка возникает из-за того, что django-revproxy пытается прочитать необработанное тело запроса, чтобы он мог создать прокси-запрос к вышестоящему серверу.

Однако с семантикой Django (и WSGI, и буферизацией)это невозможно, если вы обращаетесь к телу запроса как к , но к необработанному потоку, который вы делаете, когда вы request.data.get('name').Это анализирует тело запроса как JSON, HTTP multipart, независимо от того, что зависит от конфигурации согласования запроса DRF, и использует поток.

Насколько я понимаю, есть два способа обойти это:

  • передать bot_name куда-то еще, кроме тела;например, параметр строки запроса, HTTP-заголовок, часть URL-адреса, поэтому вам не нужно обращаться к телу, или
  • создайте внутренний запрос самостоятельно, используя requests вместо обратного проксирования (что в основном то же самое, но с добавленной магией, чтобы попытаться скопировать запрос как есть).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...