Django Cookiecutter Channels3 - соединение открывается, но не отправляется

Я начал проект с Django Cookiecutter с Docker: https://cookiecutter-django.readthedocs.io/en/latest/

Я пытаюсь добавить каналы и следую руководству в их документации: https://channels.readthedocs.io/en/stable/tutorial

Я добавил Channels 3.0.4 в requirements.txt, пересобрал docker контейнер.

Я добавил каналы в settings/base.py, и вот что:

WSGI_APPLICATION = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"

Я обновил свой файл config/asgi.py:

import os
import sys
from pathlib import Path

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from the_pub.chat import routing

ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(ROOT_DIR / "the_pub"))

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")

django_application = get_asgi_application()

from config.websocket import websocket_application  # noqa isort:skip

application = ProtocolTypeRouter({
    "https": django_application,
    "websocket": AuthMiddlewareStack(
        URLRouter(
            routing.websocket_urlpatterns
        )
    ),
})

async def application(scope, receive, send):
    if scope["type"] == "http":
        await django_application(scope, receive, send)
    elif scope["type"] == "websocket":
        await websocket_application(scope, receive, send)
    else:
        raise NotImplementedError(f"Unknown scope type {scope['type']}")

создан файл config/websocket.io

async def websocket_application(scope, receive, send):
    while True:
        event = await receive()

        if event["type"] == "websocket.connect":
            await send({"type": "websocket.accept"})

        if event["type"] == "websocket.disconnect":
            break

        if event["type"] == "websocket.receive":
            if event["text"] == "ping":
                await send({"type": "websocket.send", "text": "pong!"})

просмотров:

# chat/views.py
from django.shortcuts import render

def index(request):
    return render(request, 'chat/index.html')
  
def index(request):
    return render(request, 'chat/index.html', {})

def room(request, room_name):
    return render(request, 'chat/room.html', {
        'room_name': room_name
    })

маршрутизация:

# chat/routing.py
from django.urls import re_path
from the_pub.chat import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

чат/урлы:

# chat/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('<str:room_name>/', views.room, name='room'),
]

потребитель:

# chat/consumers.py
import json
from channels.generic.websocket import WebsocketConsumer

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        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
        }))

app.py

from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class ChatConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'the_pub.chat'
    verbose_name= _("Chat")

    def ready(self):
        try:
            import the_pub.users.signals  # noqa F401
        except ImportError:
            pass

template:

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Room</title>
</head>
<body>
    <textarea id="chat-log" cols="100" rows="20"></textarea><br>
    <input id="chat-message-input" type="text" size="100"><br>
    <input id="chat-message-submit" type="button" value="Send">
    {{ room_name|json_script:"room-name" }}
    <script>
        const roomName = JSON.parse(document.getElementById('room-name').textContent);

        const chatSocket = new WebSocket(
            'ws://'
            + window.location.host
            + '/ws/chat/'
            + roomName
            + '/'
        );

        chatSocket.onmessage = function(e) {
            const data = JSON.parse(e.data);
            document.querySelector('#chat-log').value += (data.message + '\n');
            console.log(data);
        };

        chatSocket.onclose = function(e) {
            console.error('Chat socket closed unexpectedly');
        };

        document.querySelector('#chat-message-input').focus();
        document.querySelector('#chat-message-input').onkeyup = function(e) {
            if (e.keyCode === 13) {  // enter, return
                document.querySelector('#chat-message-submit').click();
            }
        };

        document.querySelector('#chat-message-submit').onclick = function(e) {
            const messageInputDom = document.querySelector('#chat-message-input');
            const message = messageInputDom.value;
            chatSocket.send(JSON.stringify({
                'message': message
            }));
            messageInputDom.value = '';
        };
    </script>
</body>
</html>

Когда я тестирую вебсокет в плагине chrome, я могу отправлять сообщения и они записываются в журнал. Когда я нажимаю кнопку "Отправить" на форме, ничего не происходит. Ни консольных предупреждений, ни ничего в журналах docker. Все, что он делает, это очищает текст в текстовом поле. Я не думал, что третья сторона может проверить сокет, потому что я обернул его в слой аутентификации, но все наоборот, мое приложение ведет себя так, как будто javascript для отправки сообщения в сокет не существует.

При установке Channels говорится, что нужно сделать 'pip -m install -U channels'. Я добавил каналы в базовый файл requirements.txt и позволил django cookiecutter запустить установку с остальными библиотеками. это сломало его?

Также, я запускаю этот проект, он был установлен с помощью cookiecutter, который, я полагаю, является wsgi. Возможно ли вообще так использовать wsgi и asgi или мне следует искать способ запустить весь сайт на asgi?

В консоли появляется ошибка "DevTools failed to load source map: Could not load content for /requestProvider.js.map". Обычно я игнорирую эти ошибки, но эта подозрительно связана с функцией socket.send() при срабатывании .onmessage.

  • Вы должны убрать async def application(...) из config/asgi.py, поскольку вы определяете приложение как переменную прямо выше.

  • При использовании каналов, файл config/websocket.py не совсем нужен, так как вы обрабатываете websocket соединения от потребителей.

  • Убедитесь, что channels_redis установлен, если вы используете Redis для канального уровня.

  • Ваш chat/views.py имеет два индексных представления. Не уверен, что так и задумано, но это создаст ненужную путаницу, когда это не будет работать.

    .
Вернуться на верх