django-websocket打造简单的聊天室

文章展示了如何使用Django的channels库和Vue.js构建一个实时的在线聊天室。前端通过Vue.js处理用户输入和显示聊天记录,后端利用Django的WebSockets处理通信。在部署过程中遇到的问题是由于channels版本导致的asgi_application无法读取,通过降级channels到3.0.1解决了该问题。
摘要由CSDN通过智能技术生成

1,框架DJANGO,VUE 

2,前端VUE代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>聊天窗口</title>
    {% load static %}
    <script src="{% static '/vue/vue.js'%}"></script>
</head>
<style>
    *{margin:0;padding:0}
    .head1{
        width: 600px;
        height: 1000px;
        position: absolute;
        left:50%; top:50%;
        transform:translate(-50%,-50%);
        margin-top: 200px;
        /* border: 6px solid rgb(148, 241, 26);
        border-radius: 2px;
        border-color: rgb(187, 248, 248); */
    }
    .head2{
        width: 600px;
        height: 400px;
        border: 6px solid red;
        border-color: rgb(187, 248, 248);
    }
    .head3{
        width: 600px;
        height: 200px;
        border-color: aqua;
        border: 6px solid rgb(245, 164, 42);
    }
    .chatlog{
        width: 600px;
        height: 400px;
    }
</style>
<body>
    <div id="go" class="head1">
        <h1>留言窗口</h1>
        <div class="head2">
            <textarea id="chat-log" class='chatlog' v-model="textareacontent" cols="100" rows="20"  v-on:keyup.enter="search"></textarea><br>
        </div>
        <div class="head3">
            <button class="button1"  @click="sendinfo">点击发送</button>
            <div>
                <input v-model="content" type="text" placeholder="此处聊天" >
            </div>
        </div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip=false
        new Vue ({
            el:'#go',
            data:{
                name:'🐏',
                content:'',
                roomName:'room1',
                textareacontent:"",
            },
          created() {
            this.initWebSocket();
          },
          destroyed() {
            this.websock.close() //离开路由之后断开websocket连接
          },
          methods: {
            initWebSocket(){ //初始化weosocket
            //const roomName = JSON.parse(document.getElementById('room-name').textContent);
            const wss_protocol = (window.location.protocol == 'https:') ? 'wss://': 'ws://';
            const wsuri =  wss_protocol + window.location.host + '/ws/chat/'  + this.roomName + '/';
            this.websock = new WebSocket(wsuri);
            this.websock.onmessage = this.websocketonmessage;
            this.websock.onopen = this.websocketonopen;
            this.websock.onerror = this.websocketonerror;
            this.websock.onclose = this.websocketclose;
            },
            websocketonopen(){ //连接建立之后执行send方法发送数据
              let actions = {"message":'[公告]欢迎来到讨论群。请文明发言!\n'};
              this.websocketsend(JSON.stringify(actions));
            },
            websocketonerror(){//连接建立失败重连
              this.initWebSocket();
            },
            websocketonmessage(e){ //数据接收
              const redata = JSON.parse(e.data);
              this.textareacontent+=(redata.message+'\n')
            },
            websocketsend(Data){//数据发送
              this.websock.send(Data);
            },
            websocketclose(e){  //关闭
              console.log('断开连接',e);
            },
            sendinfo(){
                this.websocketsend(JSON.stringify({message:this.content}));
            }
          },
        })
      </script>

</body>
</html>

3,后端DJANGO代码

consumers.py

import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import datetime

class ChatConsumer(WebsocketConsumer):
    # websocket建立连接时执行方法
    def connect(self):
        # 从url里获取聊天室名字,为每个房间建立一个频道组
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name
        # 将当前频道加入频道组
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )

        # 接受所有websocket请求
        self.accept()

    # websocket断开时执行方法
    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )

    # 从websocket接收到消息时执行函数
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        # 发送消息到频道组,频道组调用chat_message方法
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # 从频道组接收到消息后执行方法
    def chat_message(self, event):
        message = event['message']
        datetime_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        # 通过websocket发送消息到客户端
        self.send(text_data=json.dumps({
            'message': f'{datetime_str}:{message}'
        }))

routing.py

from django.urls import re_path
from . import consumers

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

asgi.py

"""
ASGI config for admin_demo project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""

# import os
#
# from django.core.asgi import get_asgi_application
#
# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'admin_demo.settings')
#
# application = get_asgi_application()
import os

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

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "admin_demo.settings")

application = ProtocolTypeRouter({
    # http请求使用这个
    "http": get_asgi_application(),
    # websocket请求使用这个
    "websocket": AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),
})

setting.py    静态文件配置

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(os.path.dirname(__file__),'static')
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
('css',os.path.join(STATIC_ROOT,'css').replace('\\','/') ),
('js',os.path.join(STATIC_ROOT,'js').replace('\\','/') ),
('images',os.path.join(STATIC_ROOT,'images').replace('\\','/') ),
('upload',os.path.join(STATIC_ROOT,'upload').replace('\\','/') ),
)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')

#跨域增加忽略
CORS_ALLOW_CREDENTIALS = True  # 允许携带Cookie
CORS_ORIGIN_ALLOW_ALL = True
#channel配置
ASGI_APPLICATION = 'admin_demo.asgi.application'
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)

CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'Pragma',
)

4,卡点在于前后端websocket接口的用法协调性需要保持一致,后端django怎么单纯的应用vue.js,而不去用原生的jquery(非主流用法)

引用链接Django实战:channels + websocket四步打造个在线聊天室(附动图) - 知乎

此处博主用的是原生写法,我用Vue进行改写

5,大坑之线上部署:

一直出现无法读取asgi_application .网上解决办法都一样。主要原因还是包的原因.parse版本过高。  as_asgi()  问题主要是因为channels版本的问题,目前我用3.0.1就没问题,其他的不太靠谱

python3 -m pip install urllib3==1.26.6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值