【问题标题】:Django 3.0 + Channels + ASGI + TokenAuthMiddlewareDjango 3.0 + 频道 + ASGI + TokenAuthMiddleware
【发布时间】:2020-01-31 18:20:23
【问题描述】:

我升级到 Django 3.0,现在使用 websockets + TokenAuthMiddleware 时出现此错误:

SynchronousOnlyOperation
You cannot call this from an async context - use a thread or sync_to_async.

【问题讨论】:

    标签: django django-channels asgi


    【解决方案1】:

    问题是您无法从异步上下文访问同步代码。这是 Django 3.0 的TokenAuthMiddleware

    # myproject.myapi.utils.py
    from channels.auth import AuthMiddlewareStack
    from channels.db import database_sync_to_async
    from django.contrib.auth.models import AnonymousUser
    
    from rest_framework.authtoken.models import Token
    
    
    @database_sync_to_async
    def get_user(headers):
        try:
            token_name, token_key = headers[b'authorization'].decode().split()
            if token_name == 'Token':
                token = Token.objects.get(key=token_key)
                return token.user
        except Token.DoesNotExist:
            return AnonymousUser()
    
    
    class TokenAuthMiddleware:
    
        def __init__(self, inner):
            self.inner = inner
    
        def __call__(self, scope):
            return TokenAuthMiddlewareInstance(scope, self)
    
    
    class TokenAuthMiddlewareInstance:
        """
        Yeah, this is black magic:
        https://github.com/django/channels/issues/1399
        """
        def __init__(self, scope, middleware):
            self.middleware = middleware
            self.scope = dict(scope)
            self.inner = self.middleware.inner
    
        async def __call__(self, receive, send):
            headers = dict(self.scope['headers'])
            if b'authorization' in headers:
                self.scope['user'] = await get_user(headers)
            inner = self.inner(self.scope)
            return await inner(receive, send)
    
    
    TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
    

    像这样使用它:

    # myproject/routing.py
    from myapi.utils import TokenAuthMiddlewareStack
    from myapi.websockets import WSAPIConsumer
    
    application = ProtocolTypeRouter({
        "websocket": TokenAuthMiddlewareStack(
            URLRouter([
                path("api/v1/ws", WSAPIConsumer),
            ]),
        ),
    
    })
    application = SentryAsgiMiddleware(application)
    

    【讨论】:

    • 请将此标记为答案。完美无瑕!如果我以前发现这个,我就不会浪费时间了。
    • 它不再起作用了:TypeError: __call__() missing 2 required positional arguments: 'receive' and 'send'
    【解决方案2】:

    作为@tapion stated,这个解决方案不再适用,因为channels 3.x

    新的解决方案可以稍微调整一下:

    class TokenAuthMiddleware:
        def __init__(self, inner):
            self.inner = inner
    
        async def __call__(self, scope, receive, send):
            headers = dict(scope['headers'])
            if b'authorization' in headers:
                scope['user'] = await get_user(headers)
            return await self.inner(scope, receive, send)
    

    【讨论】:

      猜你喜欢
      • 2020-03-31
      • 2019-10-16
      • 2020-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-07
      • 2022-08-12
      相关资源
      最近更新 更多