Пользовательское промежуточное ПО Django каналов не завершает обработку до подключения Websocket - PullRequest
0 голосов
/ 01 апреля 2020

У меня есть приложение WSGI, к которому я добавляю Django Каналы, чтобы обеспечить функциональность веб-сокета. Я создал потребителя с помощью WebsocketConsumer, добавил пользовательское промежуточное ПО в файл маршрутизации и реализовал базовую c версию извлечения токена из входящего запроса на подключение. Я могу успешно распечатать токен, который находится в базе данных, поэтому я знаю, что передается правильная информация.

Я могу подключиться к сокету, но он всегда возвращается как анонимный пользователь в области действия. Похоже, что функция get_user_from_token не получает возможности выполнить до того, как выполнится функция соединения, потому что все отпечатки в функции __call__ класса TokenAuthMiddleware печатаются, и ни один из отпечатков из get_user_from_Token не печатается. Я попытался переключить потребителя на асин c, но это открыло целый ряд других проблем, которые я не мог понять. Я попытался поместить async перед __call__ и await перед вызовом функции, но это тоже не сработало. Текущая ошибка, которую я получаю:

Exception inside application: 'coroutine' object has no attribute '_wrapped'
  File "C:\Users\PC\Envs\p3\lib\site-packages\channels\sessions.py", line 183, in __call__
    return await self.inner(receive, self.send)
  File "C:\Users\PC\Envs\p3\lib\site-packages\channels\middleware.py", line 40, in coroutine_
call
    await self.resolve_scope(scope)
  File "C:\Users\PC\Envs\p3\lib\site-packages\channels\auth.py", line 166, in resolve_scope
    scope["user"]._wrapped = await get_user(scope)
  'coroutine' object has no attribute '_wrapped'

Как мне получить промежуточное ПО до fini sh, что он делает до того, как connect попытается проверить пользователя?

my_app / routing.py

from channels.routing import ProtocolTypeRouter, URLRouter
import api.channels.routing
from my_app.ws_token_auth import TokenAuthMiddlewareStack


application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': TokenAuthMiddlewareStack(
        URLRouter(
            api.channels.routing.websocket_urlpatterns
        )
    ),
})

API / каналы / consumer.py

import json
from asgiref.sync import async_to_sync
from channels.db import database_sync_to_async

from channels.generic.websocket import WebsocketConsumer

class HeaderConsumer(WebsocketConsumer):
    def connect(self):
        if self.scope["user"].is_anonymous:
            # Reject the connection
            print('rejected')
            self.close()
        else:
            self.accept()

        self.user = self.scope['user']
        self.message_threads = set()

    def disconnect(self, code):
        """
        Called when the WebSocket closes for any reason.
        """
        # Leave all the rooms we are still in
        for thread_id in list(self.message_threads):
            try:
                self.leave_thread(thread_id)
            except ClientError:
                pass

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        self.send(text_data=json.dumps({
            'message': message + message
        }))

my_app / ws_token_auth.py

from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections

@database_sync_to_async
def close_connections():
    close_old_connections()

@database_sync_to_async
def get_user_from_token(t):
    try:
        print("trying token" + t)
        token = Token.objects.get(token=t).prefetch_related('user')
        return token.user
    except Token.DoesNotExist:
        print("failed")
        return AnonymousUser()

class TokenAuthMiddleware:
    """
    Token authorization middleware for Django Channels 2
    """

    def __init__(self, inner):
        self.inner = inner

    def __call__(self, scope):
        close_connections()
        print("hi")

        headers = dict(scope['headers'])

        if b'cookie' in headers:
            pieces = headers[b'cookie'].decode().split("; ")
            key_values = {i.split('=', 1)[0]: i.split('=', 1)[1] for i in pieces}
            print("x")
            if 'token' in key_values:
                try:
                    scope['token'] = key_values['token']
                    print("y")
                    user = get_user_from_token(key_values['token'])
                    print("z")
                except Token.DoesNotExist:
                    print("no token")
                    user = AnonymousUser()
            else:
                print("no token?")
        else:
            print("no cookie")
        return self.inner(dict(scope, user=user))


TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
...