django可以通过channels或者dwebsocket去实现,但是dwebsocket不支持django3.x,所以使用channels来实现前后端的通信, 实现功能 建立群聊
一、各种前后端配置
1、django安装
安装指定版本的包
pip install channels==3.0.4
pip install channels_redis
2、vue安装:
使用chat_ui插件
npm install --save @maybecode/m-chat
同时配置到main中:
import MChat from '@maybecode/m-chat'
Vue.use(MChat)
3、django中注册channels
在配置文件settings的INSTALLED_APPS中加入'channels'
4、在setting中配置asgi
这里为什么用asgi不用wsgi,因为wsgi不支持websocket通信。
WSGI_APPLICATION = 'dyz_account.wsgi.application'
ASGI_APPLICATION = 'dyz_account.asgi.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
# 自己的redis地址
"hosts": [('127.0.0.1', 6379)],
},
},
}
二、前后端的主要代码
1、在主项目下修改或添加asgi.py文件
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from . import routing
from channels.auth import AuthMiddlewareStack
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dyz_account.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)),
})
2、在settings同级目录中创建routing.py。注册路由
from django.urls import path
from dyz_account.consumers import ChatConsumer
websocket_urlpatterns = [
path('ws/chat', ChatConsumer.as_asgi()),
]
3、创建consumer.py文件,处理websocket通信逻辑
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
CONN_LIST = []
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
print("开始链接...")
# 有客户端来向后端发送websocket连接的请求时,自动触发。
# 服务端允许和客户端创建连接(握手)。
self.accept()
CONN_LIST.append(self)
def websocket_receive(self, message):
print('22222222', message)
data = json.loads(message.get('text', '{}'))
chat_type = data.get('chat_type')
chat_id = data.get('chat_id')
chat_content = data.get('message')
if chat_type == 'add_chat':
async_to_sync(self.channel_layer.group_add)(chat_id, self.channel_name)
else:
async_to_sync(self.channel_layer.group_send)(chat_id, {"type": 'chat.message', 'message': message})
def chat_message(self, event):
self.send(event['message']['text'])
def websocket_disconnect(self, message):
CONN_LIST.remove(self)
raise StopConsumer()
4、前端进行websocket连接,
<template>
<div style="width: 60%; margin-left: 20%">
<div class="message" id="message"></div>
<div v-show="add_chat">
<input
type="text"
placeholder="请输入群聊id"
id="txt"
v-model="chat_id"
/>
<input type="button" value="加入群聊" @click="addChat()" />
</div>
<div v-show="send_chat">
<m-chat
ref="mChat"
:messages="messages"
@submit="submit"
:loadMore="loadMore"
height="100vh"
:openExtends="open_extends"
>
</m-chat>
</div>
<div>
</div>
</div>
</template>
<script>
// import { } from "@/components/axios_api/api.js"; // @符号指的是src路径
import MChat from "@maybecode/m-chat";
export default {
data() {
return {
user_id: "",
send_data: "",
chat_id: null,
send_chat: false,
add_chat: true,
messages: [],
open_extends: ["image"],
};
},
components: {
MChat,
},
methods: {
submit(content) {
this.sendMessage(content["content"]);
},
loadMore() {},
// 加入群聊,如果有对应的id,进入群聊,没有则创建一个新的群聊
addChat() {
var userid = localStorage.getItem("user_id");
if (userid) {
if (this.chat_id) {
this.initWebSocket();
this.send_chat = true;
this.add_chat = false;
} else {
alert("请输入群聊号");
}
}else{
alert('拜拜')
this.$router.push('/login')
}
},
initWebSocket() {
//初始化websocket 可以使用自己的ip地址
var wsuri = "ws://127.0.0.1:8000";
// var ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";
var ws_on_line = wsuri + "/ws/chat";
// 本地走代理/vue.config.js,线上也代理nginx代理
console.log("111111111111", ws_on_line);
// var ws_scheme = window.location.protocol==="https:"?"wss":"ws"
this.websock = new WebSocket(ws_on_line);
this.websock.onopen = this.websocketonopen;
this.websock.onmessage = this.websocketonmessage;
this.websock.onerror = this.websocketonerror;
// this.websock.onclose = this.websocketclose;
},
websocketonopen() {
//连接建立之后执行send方法发送数据
console.log("建立连接");
var actions = { message: "连接测试" };
var type = "add_chat";
this.sendMessage(actions, type);
},
websocketonerror(e) {
//连接建立失败重连
console.log("连接error", e);
// this.initWebSocket();
},
websocketonmessage(e) {
//数据接收
this.websocket_data = JSON.parse(e.data);
console.log("websocket-res", JSON.stringify(this.websocket_data));
console.log("接收后端数据type", typeof this.websocket_data);
let message_user_id = this.websocket_data["username"];
let message_self = false;
// 判断当前消息是否自己发出
if (message_user_id == localStorage.getItem("username")) {
message_self = true;
}else{
message_self = false
}
let name = this.websocket_data.username;
// 将websocket消息展示在消息提示框
var count = this.messages.length;
var push_message = {
type: "text",
content: { text: JSON.stringify(this.websocket_data.message) },
id: count + 1,
self: message_self,
name: name,
};
this.messages.push(push_message);
var h = this.$createElement;
// 创建html元素
this.hrender = h("p", null, [
h(
"div",
[
h("div", JSON.stringify(this.websocket_data.message)),
// 传变量
// },
// }),
],
null
),
]);
},
sendMessage(Data, type = "send_data") {
//数据发送
console.log("222222222222");
this.websock.send(
JSON.stringify({
chat_id: this.chat_id,
message: Data,
chat_type: type,
username: localStorage.getItem("username"),
user_id: localStorage.getItem("user_id"),
//在登录的时候进行保存信息
})
);
},
websocketclose(e) {
//关闭
console.log("断开连接", e);
},
userinfo() {},
},
mounted() {},
};
</script>
<style>
</style>
5、需要在登录时保存用户信息
<template>
<p>账号:<input type="text" v-model="user"></p>
<p>密码:<input type="password" v-model="pwd"></p>
<p><button @click="login">登录</button></p>
</template>
<script>
import {post} from "../../utils/request"
export default {
data(){
return{
user:"",
pwd:""
}
},
methods:{
login(){
post("/user/sms_phone/",{username:this.user,password:this.pwd}).then(resp=>{
console.log(resp)
if(resp.status==200){
console.log(resp.data.token)
localStorage.setItem("username", resp.data.username)
localStorage.setItem('user_id',resp.data.user_id)
localStorage.setItem('token',resp.data.token)
console.log('-------------',resp.data.username,resp.data.user_id)
alert("登录成功")
}
}).catch(err=>{
console.log(err)
})
},
}
}
</script>
<style>
</style>
6、参考相关连接地址
m-chat: 基于vue 聊天(IM) UI组件https://gitee.com/null_639_5368/m-chat#popitemclick-demo