Django 通道自定义身份验证中间件 __call__() 缺少 2 个必需的位置参数:'receive' 和 'send'
Django Channel Custom Authentication Middleware __call__() missing 2 required positional arguments: 'receive' and 'send'
我正在为 django 通道编写自定义身份验证中间件
class TokenAuthMiddleware:
def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner
def __call__(self, scope):
return TokenAuthMiddlewareInstance(scope, self)
class TokenAuthMiddlewareInstance:
def __init__(self, scope, middleware):
self.middleware = middleware
self.scope = dict(scope)
self.inner = self.middleware.inner
async def __call__(self, receive, send):
## my logic to get validate user and store the user in user data
...
...
...
self.scope['user'] = user_data
inner = self.inner(self.scope)
return await inner(receive, send)
但是在尝试从前端连接到 Web 套接字时出现以下错误
TypeError: __call__() missing 2 required positional arguments: 'receive' and 'send'
我使用 Django Channels 3 设法让它以这种方式工作:
from django.contrib.auth.models import AnonymousUser
from rest_framework.authtoken.models import Token
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
import urllib.parse
@database_sync_to_async
def get_user(token):
try:
token = Token.objects.get(key=token)
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):
decoded_qs = urllib.parse.parse_qs(self.scope["query_string"])
if b'token' in decoded_qs:
token = decoded_qs.get(b'token').pop().decode()
self.scope['user'] = await get_user(token)
return await self.inner(self.scope, receive, send)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
供您参考:https://channels.readthedocs.io/en/stable/releases/3.0.0.html
变化自 routing.py
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]
至
消费者现在有一个 as_asgi() class 方法,您需要在设置路由时调用该方法:
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
然后
如果您需要自定义身份验证 https://channels.readthedocs.io/en/stable/topics/authentication.html#custom-authentication
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model
User = get_user_model()
@database_sync_to_async
def get_user(user_id):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return AnonymousUser()
class QueryAuthMiddleware:
"""
Custom middleware (insecure) that takes user IDs from the query string.
"""
def __init__(self, app):
# Store the ASGI application we were passed
self.app = app
async def __call__(self, scope, receive, send):
# Look up user from query string (you should also do things like
# checking if it is a valid user ID, or if scope["user"] is already
# populated).
scope['user'] = await get_user(int(scope["query_string"]))
return await self.app(scope, receive, send)
TokenAuthMiddlewareStack = lambda inner: QueryAuthMiddleware(AuthMiddlewareStack(inner))
use requirements.txt as following list, and also download package in this order
Django==3.0.8
djangorestframework==3.11.0
websocket-client==0.57.0
redis==3.5.3
asgiref==3.2.10
channels-redis==2.4.2
channels==3.0.1
正如 Yuva Raja 所说,在 Django Channels version 3 中,您需要将路径设置为:
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
还有一件事,他们在 Custom authentication official documentation 中忘记使用 scope 而不是 self.scope。
所以一定要使用:
scope['user'] = await get_user(int(scope["query_string"]))
而不是他们的例子:
scope['user'] = await get_user(int(self.scope["query_string"]))
from urllib.parse import parse_qs
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from channels.auth import AuthMiddlewareStack
from rest_framework_simplejwt.tokens import AccessToken
from channels.db import database_sync_to_async
User = get_user_model()
@database_sync_to_async
def get_user(user_id):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return AnonymousUser()
class TokenAuthMiddleware:
def __init__(self, inner):
self.inner = inner
async def __call__(self, scope, receive, send):
close_old_connections()
query_string = parse_qs(scope['query_string'].decode())
token = query_string.get('token')
if not token:
scope['user'] = AnonymousUser()
return await self.inner(scope, receive, send)
access_token = AccessToken(token[0])
user = await get_user(access_token['id'])
if isinstance(user, AnonymousUser):
scope['user'] = AnonymousUser()
return await self.inner(scope, receive, send)
if not user.is_active:
scope['user'] = AnonymousUser()
return await self.inner(scope, receive, send)
scope['user'] = user
return await self.inner(scope, receive, send)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
这个错误发生在我安装的时候channels==2.4.0
将频道更新到 channels==3.0.3
(目前最新)解决了这个问题!
我正在为 django 通道编写自定义身份验证中间件
class TokenAuthMiddleware:
def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner
def __call__(self, scope):
return TokenAuthMiddlewareInstance(scope, self)
class TokenAuthMiddlewareInstance:
def __init__(self, scope, middleware):
self.middleware = middleware
self.scope = dict(scope)
self.inner = self.middleware.inner
async def __call__(self, receive, send):
## my logic to get validate user and store the user in user data
...
...
...
self.scope['user'] = user_data
inner = self.inner(self.scope)
return await inner(receive, send)
但是在尝试从前端连接到 Web 套接字时出现以下错误
TypeError: __call__() missing 2 required positional arguments: 'receive' and 'send'
我使用 Django Channels 3 设法让它以这种方式工作:
from django.contrib.auth.models import AnonymousUser
from rest_framework.authtoken.models import Token
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
import urllib.parse
@database_sync_to_async
def get_user(token):
try:
token = Token.objects.get(key=token)
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):
decoded_qs = urllib.parse.parse_qs(self.scope["query_string"])
if b'token' in decoded_qs:
token = decoded_qs.get(b'token').pop().decode()
self.scope['user'] = await get_user(token)
return await self.inner(self.scope, receive, send)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
供您参考:https://channels.readthedocs.io/en/stable/releases/3.0.0.html
变化自 routing.py
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]
至 消费者现在有一个 as_asgi() class 方法,您需要在设置路由时调用该方法:
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
然后 如果您需要自定义身份验证 https://channels.readthedocs.io/en/stable/topics/authentication.html#custom-authentication
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model
User = get_user_model()
@database_sync_to_async
def get_user(user_id):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return AnonymousUser()
class QueryAuthMiddleware:
"""
Custom middleware (insecure) that takes user IDs from the query string.
"""
def __init__(self, app):
# Store the ASGI application we were passed
self.app = app
async def __call__(self, scope, receive, send):
# Look up user from query string (you should also do things like
# checking if it is a valid user ID, or if scope["user"] is already
# populated).
scope['user'] = await get_user(int(scope["query_string"]))
return await self.app(scope, receive, send)
TokenAuthMiddlewareStack = lambda inner: QueryAuthMiddleware(AuthMiddlewareStack(inner))
use requirements.txt as following list, and also download package in this order
Django==3.0.8
djangorestframework==3.11.0
websocket-client==0.57.0
redis==3.5.3
asgiref==3.2.10
channels-redis==2.4.2
channels==3.0.1
正如 Yuva Raja 所说,在 Django Channels version 3 中,您需要将路径设置为:
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
还有一件事,他们在 Custom authentication official documentation 中忘记使用 scope 而不是 self.scope。 所以一定要使用:
scope['user'] = await get_user(int(scope["query_string"]))
而不是他们的例子:
scope['user'] = await get_user(int(self.scope["query_string"]))
from urllib.parse import parse_qs
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from channels.auth import AuthMiddlewareStack
from rest_framework_simplejwt.tokens import AccessToken
from channels.db import database_sync_to_async
User = get_user_model()
@database_sync_to_async
def get_user(user_id):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return AnonymousUser()
class TokenAuthMiddleware:
def __init__(self, inner):
self.inner = inner
async def __call__(self, scope, receive, send):
close_old_connections()
query_string = parse_qs(scope['query_string'].decode())
token = query_string.get('token')
if not token:
scope['user'] = AnonymousUser()
return await self.inner(scope, receive, send)
access_token = AccessToken(token[0])
user = await get_user(access_token['id'])
if isinstance(user, AnonymousUser):
scope['user'] = AnonymousUser()
return await self.inner(scope, receive, send)
if not user.is_active:
scope['user'] = AnonymousUser()
return await self.inner(scope, receive, send)
scope['user'] = user
return await self.inner(scope, receive, send)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
这个错误发生在我安装的时候channels==2.4.0
将频道更新到 channels==3.0.3
(目前最新)解决了这个问题!