Исключение внутри приложения: 'user' user=self.scope['user'] KeyError: 'user'
Я пытаюсь установить пользовательское промежуточное ПО для моих каналов django, поскольку AuthMiddleWare возвращает anonymousUser при попытке использовать self.scope['user] в consumers.py, и я использую аутентификацию на основе токенов, но после установки пользовательского промежуточного ПО django продолжает бросать это исключение:
Exception inside application: 'user'
Traceback (most recent call last):
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\security\websocket.py", line 37, in __call__
return await self.application(scope, receive, send)
File "C:\Users\user\Easylance\chat\token_auth.py", line 29, in __call__
return await super().__call__(scope, receive, send)
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\consumer.py", line 94, in app
return await consumer(scope, receive, send)
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\consumer.py", line 58, in __call__
await await_many_dispatch(
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\consumer.py", line 73, in dispatch
await handler(message)
File "C:\Users\user\Easylance\chat\consumers.py", line 13, in websocket_connect
user=self.scope['user']
KeyError: 'user'
определенно, ошибка означает, что нет ключа для области dict с именем 'user', но в моем файле token_auth.py, где находится пользовательский промежуточный продукт, я действительно назначил ключ 'user', код пользовательского промежуточного продукта приведен ниже:
class TokenAuthMiddleware(BaseMiddleware):
def __init__(self, inner):
super().__init__(inner)
async def __call__(self, scope,receive,send):
headers = dict(scope['headers'])
if b'authorization' in headers:
try:
token_name, token_key = headers[b'authorization'].decode().split()
if token_name == 'Token':
token = Token.objects.get(key=token_key)
scope['user'] = token.user
close_old_connections()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
return await super().__call__(scope, receive, send)
Вы можете видеть в моем методе call я назначил scope['user'] как token.user, я не могу понять, что именно я сделал неправильно. Вот мой файл routing.py:
django_asgi_app = get_asgi_application()
application=ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
TokenAuthMiddleware(
URLRouter([
url(r"^chat/(?P<username>\w+)/$",SendAndReceiveMessage.as_asgi()),
])
)
),
})
мой файл consumer.py:
class SendAndReceiveMessage(AsyncConsumer):
async def websocket_connect(self, event):
other_username=self.scope['url_route']['kwargs']['username']
self.other_username=other_username
user=self.scope['user']
print(user,other_username)
unique_obj=await self.get_unique_obj(user,other_username)
self.unique_obj=unique_obj
print(unique_obj.id)
chat_id=f"unique_id_{unique_obj.id}"
self.chat_id= f"unique_id_{unique_obj.id}"
await self.channel_layer.group_add(
chat_id,
self.channel_name
)
await self.send({
"type": "websocket.accept",
})
async def websocket_receive(self,event):
print("receive",event)
new_message=event.get('message',None)
if new_message != None:
new_message=json.loads(new_message)
user=self.scope['user']
if user.is_authenticated:
username=new_message.get('username')
response={
'username':new_message.get('username'),
'message':new_message.get('text')
}
await self.channel_layer.group_send(
self.chat_id,
{
"type" : "send_message",
"received": json.dumps(response)
}
)
if user.username==username:
other_user=self.get_user_obj(self.other_username)
await self.add_message(self.unique_obj,user, other_user, new_message.get('text'))
else:
other_user=self.get_user_obj(self.other_username)
await self.add_message(self.unique_obj,other_user,user,new_message.get('text'))
async def send_message(self,event):
await self.send({
"type":"websocket.send",
"text":event['received']
})
@database_sync_to_async
def get_unique_obj(self,user,other_username):
return UniqueHolder.objects.get_or_new(user,other_username)
@database_sync_to_async
def add_message(self,unique_obj,user1,user2,msg):
Message.objects.create(uniqueH=unique_obj,sender=user1,receiver=user2,msg=msg)
@database_sync_to_async
def get_user_obj(self,username):
return User.objects.get(username=username)
Чтобы отправить токен в веб-сокетном соединении через Postman, вы можете включить его в качестве параметра запроса в URL-адрес веб-сокета.
Например, если URL вашего вебсокета имеет вид ws://localhost:8000/path,, вы можете добавить токен в качестве параметра запроса следующим образом:
ws://localhost:8000/path?token=401f7ac837da42b97f613d789819ff93537bee6a
В Postman вы можете задать этот URL в поле "Websocket URL" при выполнении запроса websocket.
Вы можете сгенерировать токен, указав url в корневом файле urls.py и сгенерировав токен вначале:
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomObtainAuthTokenView(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data["user"]
token, created = Token.objects.get_or_create(user=user)
return Response({"token": token.key, "username": user.username})
urls.py
from .views import CustomObtainAuthTokenView
urlpatterns = [
...
path("auth/", CustomObtainAuthTokenView.as_view(), name="auth"),
...
]
Сделайте POST-запрос к пользовательскому представлению аутентификации, передав имя и пароль пользователя: через postman, или вы можете сделать это следующим образом:
curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "password123"}' http://localhost:8000/auth/
Or using a REST client like Postman:
Set the request method to POST
Set the request header Content-Type to application/json
In the request body, provide a JSON object with the username and password of the user, e.g. {"username": "admin", "password": "password123"}
Send the request to the URL http://localhost:8000/auth/
The response should contain the token, which you can use to authenticate subsequent requests.
Note: Make sure you have the djangorestframework and djangorestframework-jwt packages installed, and have them added to your INSTALLED_APPS and REST_FRAMEWORK settings in Django.