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