简体   繁体   中英

Django channels + send websocket message after post request

i have a problem with my app. My django channels and websocket works absolutely fine, when i send a message from JS(inside rendered html) the message is going into websocket and after that to the data base(asynchronously). But when i am trying send the message before POST request Render, it throws an error.

#here everything works fine:
def room(request, room_name):

    messages = message.objects.all()
    texts = []
    
    for i in messages:
        texts.append(i.text)
        
    last_lines = texts[-20:]
    
    vars = {
        "room_name": room_name,
        "messages": last_lines,
    }
    print()

    if request.method == "POST":
        vars["post_d"] = request.POST.get("desc")
        print(vars["post_d"])

        # ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
        # time.sleep(2)
        # ws.send(json.dumps({"message": message}))

        return render(request, "chat/room.html", vars)

    return render(request, "chat/room.html", vars)
#=======================================================
System check identified no issues (0 silenced).
July 12, 2021 - 08:26:23
Django version 3.1.3, using settings 'onionChat.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Post request from c# console app
HTTP POST /chat/loobby/ 200 [0.10, 127.0.0.1:3261]

My test.py file that sends python websocket message (also works well) changes shows in the db and websocket in the web browser.

import json
import time
import asyncio
from websocket import create_connection

async def send(message):
    ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
    time.sleep(2)
    ws.send(json.dumps({"message": message}))
    print("here")

if __name__ == "__main__":
    asyncio.run(send("nothing"))

#==================================================================
System check identified no issues (0 silenced).
July 12, 2021 - 08:29:48
Django version 3.1.3, using settings 'onionChat.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
WebSocket HANDSHAKING /ws/chat/loobby/ [127.0.0.1:1032]
WebSocket CONNECT /ws/chat/loobby/ [127.0.0.1:1032]
WebSocket DISCONNECT /ws/chat/loobby/ [127.0.0.1:1032]
added message to db

But when i am trying to send websocket message AND after that return render it throws an error

def room(request, room_name):

    messages = message.objects.all()
    texts = []

    for i in messages:
        texts.append(i.text)

    last_lines = texts[-20:]

    vars = {
        "room_name": room_name,
        "messages": last_lines,
    }
    print()

    if request.method == "POST":
        vars["post_d"] = request.POST.get("desc")
        print(vars["post_d"])

        ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
        time.sleep(2) 
        ws.send(json.dumps({"message": message}))

    return render(request, "chat/room.html", vars)
#==================================================================
System check identified no issues (0 silenced).
July 12, 2021 - 08:34:51
Django version 3.1.3, using settings 'onionChat.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Post request from c# console app
WebSocket HANDSHAKING /ws/chat/loobby/ [127.0.0.1:30427]
WebSocket DISCONNECT /ws/chat/loobby/ [127.0.0.1:30427]
Internal Server Error: /chat/loobby/
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\site-packages\asgiref\sync.py", line 339, in thread_handler
    raise exc_info[1]
  File "C:\Program Files\Python39\lib\site-packages\django\core\handlers\exception.py", line 38, in inner
    response = await get_response(request)
  File "C:\Program Files\Python39\lib\site-packages\django\core\handlers\base.py", line 231, in _get_response_async
    response = await wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Program Files\Python39\lib\site-packages\asgiref\sync.py", line 304, in __call__
    ret = await asyncio.wait_for(future, timeout=None)
  File "C:\Program Files\Python39\lib\asyncio\tasks.py", line 442, in wait_for
    return await fut
  File "C:\Program Files\Python39\lib\concurrent\futures\thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "C:\Program Files\Python39\lib\site-packages\asgiref\sync.py", line 343, in thread_handler
    return func(*args, **kwargs)
  File "C:\Users\Алексей\Desktop\dev\OnionChat\onionChat\chat\views.py", line 48, in room
    ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_core.py", line 595, in create_connection
    websock.connect(url, **options)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_core.py", line 252, in connect
    self.handshake_response = handshake(self.sock, *addrs, **options)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_handshake.py", line 59, in handshake
    status, resp = _get_resp_headers(sock)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_handshake.py", line 143, in _get_resp_headers
    status, resp_headers, status_message = read_headers(sock)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_http.py", line 300, in read_headers
    line = recv_line(sock)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_socket.py", line 136, in recv_line
    c = recv(sock, 1)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_socket.py", line 115, in recv
    bytes_ = _recv()
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_socket.py", line 92, in _recv
    return sock.recv(bufsize)
ConnectionResetError: [WinError 10054] Удаленный хост принудительно разорвал существующее подключение
HTTP POST /chat/loobby/ 500 [4.97, 127.0.0.1:30426]

And my c# code, but i don't think that the problem is here

static void post_command(string command)
        {
            using (var client = new WebClient())
            {
                client.Timeout = 600 * 60 * 1000;
                
                client.Headers["User-Agent"] = "Mozilla/5.0";
                var values = new NameValueCollection();
                values["desc"] = $"{command}"; 
                client.UploadValues(link, values);
                //var responseString = Encoding.Default.GetString(response);
                //Console.Write(responseString);
            }
        }

I know it's rather old question, though I faced now the same issue. It seems to be not possible to open a web-socket connection from the app that already serves such a connection. That's why your test.py works - because it's outside the Django app.

So I came up with the solution that picks up the relevant channel from currently opened socket:

views.py

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer

def room(request, room_name):
    # your logic...

    if request.method == "POST":
        vars["post_d"] = request.POST.get("desc")
        print(vars["post_d"])

        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            f'chat_{room_name}',
            {
                'type': 'receive',
                'message': message
            }
        )

Assuming that in routing.py you have:

from django.urls import re_path

from . import consumers

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

In your consumers.py you have to match receive method (note type='receive' ) and your channel name accordingly (in your example it is lobby )

from channels.generic.websocket import AsyncWebsocketConsumer


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):

        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'

        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def receive(self, text_data=None, type='receive', **kwargs):
        if isinstance(text_data, dict):
            text_data_json = text_data
        else:
            text_data_json = json.loads(text_data)

        # all other logic on handling the message...
       

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM