Django Channels - 自定义身份验证中间件引发:'coroutine' 对象不可调用
Django Channels - Custom Authentication Middleware raises: 'coroutine' object is not callable
我已经创建了自定义令牌身份验证中间件。
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from asgiref.sync import sync_to_async
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
# Close old database connections to prevent usage of timed out connections
sync_to_async(close_old_connections)()
headers = dict(scope['headers'])
try:
token_name, token_key = headers[b'sec-websocket-protocol'].decode().split(', ')
if token_name == 'Token':
token = sync_to_async(Token.objects.get, thread_sensitive=True)(key=token_name)
scope['user'] = token.user
else:
scope['user'] = AnonymousUser()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
return self.inner(scope)
当我运行它时,当我运行scope['user'] = token.user
时发生异常
[Failure instance: Traceback: <class 'AttributeError'>: 'coroutine' object has no attribute 'user'
我试过这样等待令牌查询:
token = await sync_to_async(Token.objects.get, thread_sensitive=True)(key=token_name)
并且我在 __call__
函数前面添加了异步,但是随后在 __call__
函数 运行s:[=18 中的任何代码之前引发了以下错误=]
[Failure instance: Traceback: <class 'TypeError'>: 'coroutine' object is not callable
我正在使用 Django v3.0.6 和 Django Channels v2.4.0
只需将您的函数包装在 database_sync_to_async
中,它将为您处理连接
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
<b>async def __call__(self, scope):</b>
# Close old database connections to prevent usage of timed out connections
<del>sync_to_async(close_old_connections)()</del>
headers = dict(scope['headers'])
try:
token_name, token_key = headers[b'sec-websocket-protocol'].decode().split(', ')
if token_name == 'Token':
<b>user = await self.get_user(token)</b>
scope['user'] = user
else:
scope['user'] = AnonymousUser()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
@database_sync_to_async
def get_user(self, token):
token = Token.ojbects.get(key=token)
return token.user
这是对我有用的解决方案:
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from channels.db import database_sync_to_async
@database_sync_to_async
def get_user(token):
try:
return Token.objects.get(key=token).user
except Token.DoesNotExist:
return AnonymousUser()
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner
def __call__(self, scope):
return TokenAuthMiddlewareInstance(scope, self)
class TokenAuthMiddlewareInstance:
"""
Inner class that is instantiated once per scope.
"""
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'])
token_name, token_key = headers[b'sec-websocket-protocol'].decode().split(', ')
if token_name == 'Token':
self.scope['user'] = await get_user(token_key)
else:
self.scope['user'] = AnonymousUser()
# Instantiate our inner application
inner = self.inner(self.scope)
return await inner(receive, send)
如果它对任何人有帮助,我和其他人都遇到了同样的问题。我更新了 Django 频道,然后更改了我的路由
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]
到
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
您需要 django 通道 3.0+ 才能执行此操作(https://channels.readthedocs.io/en/stable/releases/3.0.0.html). Then you can follow https://channels.readthedocs.io/en/stable/topics/authentication.html#django-authentication 以设置您的自定义中间件。
参考:
我已经创建了自定义令牌身份验证中间件。
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from asgiref.sync import sync_to_async
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
# Close old database connections to prevent usage of timed out connections
sync_to_async(close_old_connections)()
headers = dict(scope['headers'])
try:
token_name, token_key = headers[b'sec-websocket-protocol'].decode().split(', ')
if token_name == 'Token':
token = sync_to_async(Token.objects.get, thread_sensitive=True)(key=token_name)
scope['user'] = token.user
else:
scope['user'] = AnonymousUser()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
return self.inner(scope)
当我运行它时,当我运行scope['user'] = token.user
[Failure instance: Traceback: <class 'AttributeError'>: 'coroutine' object has no attribute 'user'
我试过这样等待令牌查询:
token = await sync_to_async(Token.objects.get, thread_sensitive=True)(key=token_name)
并且我在 __call__
函数前面添加了异步,但是随后在 __call__
函数 运行s:[=18 中的任何代码之前引发了以下错误=]
[Failure instance: Traceback: <class 'TypeError'>: 'coroutine' object is not callable
我正在使用 Django v3.0.6 和 Django Channels v2.4.0
只需将您的函数包装在 database_sync_to_async
中,它将为您处理连接
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
<b>async def __call__(self, scope):</b>
# Close old database connections to prevent usage of timed out connections
<del>sync_to_async(close_old_connections)()</del>
headers = dict(scope['headers'])
try:
token_name, token_key = headers[b'sec-websocket-protocol'].decode().split(', ')
if token_name == 'Token':
<b>user = await self.get_user(token)</b>
scope['user'] = user
else:
scope['user'] = AnonymousUser()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
@database_sync_to_async
def get_user(self, token):
token = Token.ojbects.get(key=token)
return token.user
这是对我有用的解决方案:
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from channels.db import database_sync_to_async
@database_sync_to_async
def get_user(token):
try:
return Token.objects.get(key=token).user
except Token.DoesNotExist:
return AnonymousUser()
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner
def __call__(self, scope):
return TokenAuthMiddlewareInstance(scope, self)
class TokenAuthMiddlewareInstance:
"""
Inner class that is instantiated once per scope.
"""
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'])
token_name, token_key = headers[b'sec-websocket-protocol'].decode().split(', ')
if token_name == 'Token':
self.scope['user'] = await get_user(token_key)
else:
self.scope['user'] = AnonymousUser()
# Instantiate our inner application
inner = self.inner(self.scope)
return await inner(receive, send)
如果它对任何人有帮助,我和其他人都遇到了同样的问题。我更新了 Django 频道,然后更改了我的路由
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]
到
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
您需要 django 通道 3.0+ 才能执行此操作(https://channels.readthedocs.io/en/stable/releases/3.0.0.html). Then you can follow https://channels.readthedocs.io/en/stable/topics/authentication.html#django-authentication 以设置您的自定义中间件。
参考: