'Django Cookiecutter Channels3 - connection opens, doesn't send

I started a a project with Django Cookiecutter w/ Docker: https://cookiecutter-django.readthedocs.io/en/latest/

I'm trying to add Channels and follow the tutorial in their docs: https://channels.readthedocs.io/en/stable/tutorial

I added Channels 3.0.4 to requirements.txt, rebuilt the docker container.

I added channels to settings/base.py, and this:

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

I updated my config/asgi.py file:

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']}")

created a config/websocket.io file

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!"})

views:

# 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
    })

routing:

# 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:

# 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'),
]

consumer:

# 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>

When I test the websocket in a chrome plugin I can send messages and it logs. When I hit send on the form it does nothing. no console alerts, nothing in docker logs. All it does is clear the text in the text box. I didn't think a third party could check the socket because I wrapped it in an authentication layer, but it's the opposite, my app acts like the javascript to send the message to the socket doesn't exist.

when you install Channels, it says to do 'pip -m install -U channels'. I added channels to the requirements.txt base file and let django cookiecutter run install with the rest of the libraries. did this break it?

Also, I'm running this project has it was set up by cookiecutter, which I guess is wsgi. Is it even possible to use both wsgi and asgi like this or should I be looking at how to run the whole site on asgi?

I get an error in the console "DevTools failed to load source map: Could not load content for /requestProvider.js.map. I normally ignore these errors but this seams suspiciously related to the socket.send() function on triggering an .onmessage.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source