使用 React 和 Django Channels 构建聊天应用程序

使用 Django 开发用于 HTTP 连接和应用程序请求的服务器很常见。 但是,在开发需要始终打开连接以进行双向连接的应用程序(如会议和聊天程序)时,使用 HTTP 连接效率低下。 在这种情况下,使用 WebSockets 是必不可少的。

通过使用 WebSockets,连接到该开放网络的所有用户都可以实时接收相关数据,这提供了一种在客户端和服务器之间建立双向连接的方法。 这是一个有状态的协议,这意味着在初始连接身份验证之后,客户端凭据被保存,并且在连接被破坏之前不需要进一步的身份验证。

在本教程中,我们将学习如何使用 Django 和 React 构建聊天应用程序。 在本教程之后,您应该更熟悉 WebSockets 在 Django 和 React 中的工作方式。 要继续阅读本文,您需要:

  • 对Django的基本了解

  • 已安装 Django v3.2

  • 对 React 的基本了解

您可以在 GitHub 上 找到完整的应用程序。 让我们开始吧!

目录

  • WebSocket 功能

  • 如何在 Django 中使用 WebSocket

  • 构建前端

  • 测试应用程序

WebSocket 功能

WebSocket 是一种双向协议,这意味着数据可以在客户端和服务器之间即时交换而不会中断。 同理,WebSockets 也被视为全双工通信。

WebSockets 不需要任何特定的浏览器即可工作; 所有浏览器都兼容。 WebSocket 是一个有状态的协议。 由于客户端凭据是在主连接验证后保存的,因此在连接丢失之前不需要再次进行额外的身份验证。

如何在 Django 中使用 WebSocket

当你想用 WebSockets 做任何事情时, Django Channels 是必不可少的,所以继续使用以下命令安装它:

pip install channels

在本节中,我们将设置 Django 以使用 WebSockets,并将其与构建普通的 Django 应用程序进行比较。

感谢 Django Channels,在 Django 中使用 WebSockets 很简单。 您可以使用 Django Channels 构建一个 ASGI (异步服务器网关接口)服务器,之后您可以构建一个组,成员可以在其中即时互相发短信。 交流不是与特定用户,而是与一个组,任何数量的用户都可以加入。

创建一个包含项目所有代码的文件夹。 导航到您刚刚在终端上创建的文件夹并运行 startproject命令来创建一个新的 Django 项目:


超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →


$ django-admin startproject chat .

现在,运行 $ python3 manage.py startapp app创建一个新的应用程序。

您需要让您的 Django 项目知道已经添加了一个新应用程序并且您安装了 Channels 插件。 您可以通过更新 chat/settings.py文件并添加 'app'到 INSTALLED_APPS列表。 它看起来像下面的代码:

# project/settings.py
INSTALLED_APPS = [
   ...
   'channels',
   'app',
]

在里面 settings.py文件,您应该设置配置以允许 Django 和 Django 通道通过消息代理相互连接。 为此,我们可以使用 Redis 之类的工具,但在此示例中,我们将坚持使用本地后端。 将以下代码行添加到您的 settings.py文件:

ASGI_APPLICATION = "chat.routing.application" #routing.py will handle the ASGI
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': "channels.layers.InMemoryChannelLayer"
        }
    }

在上面的代码中, ASGI_APPLICATION需要运行 ASGI 服务器并告诉 Django 在事件发生时该做什么。 我们将把这个配置放在一个名为 routing.py. 路由 Django Channels 类似于 Django URL 配置; 它选择将 WebSocket 请求发送到服务器时要运行的代码。

在您创建路由之前,我们将首先开发 消费者 。 在 Django Channels 中,消费者使您能够在代码中创建一组函数,这些函数将在事件发生时被调用。 它们类似于 views在姜戈中。

开发消费者,打开 app/文件夹,新建一个名为 consumers.py,并粘贴以下代码:

# app/consumers.py
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
​
class TextRoomConsumer(WebsocketConsumer):
    def connect(self):
​
        self.room_name = self.scope\['url_route'\]['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name
        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )
        self.accept()
    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )
​
    def receive(self, text_data):
        # Receive message from WebSocket
        text_data_json = json.loads(text_data)
        text = text_data_json['text']
        sender = text_data_json['sender']
        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': text,
                'sender': sender
            }
        )
​
    def chat_message(self, event):
        # Receive message from room group
        text = event['message']
        sender = event['sender']
        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'text': text,
            'sender': sender
        }))

现在,我们可以创建将处理您刚刚创建的消费者的路由。 创建一个名为的新文件 routing.py并粘贴下面的代码,这将协调消费者:

from channels.routing import ProtocolTypeRouter, URLRouter
# import app.routing
from django.urls import re_path
from app.consumers import TextRoomConsumer
websocket_urlpatterns = [
    re_path(r'^ws/(?P[^/]+)/$', TextRoomConsumer.as_asgi()),
]
# the websocket will open at 127.0.0.1:8000/ws/
application = ProtocolTypeRouter({
    'websocket':
        URLRouter(
            websocket_urlpatterns
        )
    ,
})

构建前端

现在,让我们构建一个聊天应用程序的前端,该应用程序使用 WebSockets 连接到 Django 后端。 我们将使用 React 构建这部分,并 使用 MUI 添加样式 。

在您的终端中,导航到项目的根目录并运行以下命令以获取 React 的 Create React App 样板代码:

npx create-react-app frontend

下一个, cd进入 frontend/目录并运行以下命令来安装 MUI 和 WebSocket 依赖项,这允许我们将 React 应用程序连接到 WebSocket 服务器:

npm install --save --legacy-peer-deps @material-ui/core
npm install websocket

删除所有代码 frontend/src/App.js. 我们将用本教程其余部分中的代码替换它,从初始状态开始:

import React, { Component } from 'react';
import { w3cwebsocket as W3CWebSocket } from "websocket";

class App extends Component {
  state = {
    filledForm: false,
    messages: [],
    value: '',
    name: '',
    room: 'test',
  }
  client = new W3CWebSocket('ws://127.0.0.1:8000/ws/' + this.state.room + '/'); //gets room_name from the state and connects to the backend server 
  render(){
    }

}

现在,我们需要处理当组件安装在浏览器上时发生的事情。 我们希望应用程序连接到后端服务器并在组件挂载时获取消息,因此我们将使用 componentDidMount(). 您可以通过在 render()功能:

...
componentDidMount() {
    this.client.onopen = () => {
      console.log("WebSocket Client Connected");
    };
    this.client.onmessage = (message) => {
      const dataFromServer = JSON.parse(message.data);
      if (dataFromServer) {
        this.setState((state) => ({
          messages: [
            ...state.messages,
            {
              msg: dataFromServer.text,
              name: dataFromServer.sender,
            },
          ],
        }));
      }
    };
  }
render() {
...

接下来,我们将创建用于更新状态的表单。 我们将创建一个表单来更新 name发件人和房间名称。 然后,我们将创建另一个处理表单提交的表单。 将下面的代码粘贴到 render()功能:


来自 LogRocket 的更多精彩文章:

  • 不要错过 The Replay 来自 LogRocket 的精选时事通讯

  • 了解 LogRocket 的 Galileo 如何消除噪音以主动解决应用程序中的问题

  • 使用 React 的 useEffect 优化应用程序的性能

  • 之间切换 在多个 Node 版本

  • 了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画

  • 探索 Tauri ,一个用于构建二进制文件的新框架

  • 比较 NestJS 与 Express.js


render() {
    const { classes } = this.props;
    return (
      
        {this.state.filledForm ? (
          
Room Name: {this.state.room} {this.state.messages.map((message) => ( <> ))}
{ this.setState({ value: e.target.value }); this.value = this.state.value; }} />
) : (
this.setState({ filledForm: true })} > { this.setState({ room: e.target.value }); this.value = this.state.room; }} /> { this.setState({ name: e.target.value }); this.value = this.state.name; }} />
)}
); } export default withStyles(useStyles)(App);

当您填写房间名称和寄件人姓名时, filledForm将改为 true在该状态下,将呈现输入消息的表单。 在我们的代码中,我们使用了一些需要导入的 MUI 类。 您可以通过将以下代码粘贴到您的顶部 App.js文件:

import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField";
import Container from "@material-ui/core/Container";
import Card from "@material-ui/core/Card";
import CardHeader from "@material-ui/core/CardHeader";
import Paper from "@material-ui/core/Paper";
import { withStyles } from "@material-ui/core/styles";
const useStyles = (theme) => ({
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
});

提交消息表单后,我们将在单击提交按钮时将文本发送到后端服务器。 将下面的代码直接粘贴到上面 componentDidMount()功能:

  onButtonClicked = (e) => {
    this.client.send(
      JSON.stringify({
        type: "message",
        text: this.state.value,
        sender: this.state.name,
      })
    );
    this.state.value = "";
    e.preventDefault();
  };
  componentDidMount() { 
...

测试应用程序

现在我们已经完成了应用程序的编码,我们可以对其进行测试。 首先,通过运行以下命令启动后端服务器。 确保您位于 manage.py文件是:

python manage.py runserver

在另一个终端窗口中,导航到 frontend/目录并通过运行以下命令来运行前端服务器。 React 应用程序将自动打开:

npm start

填写姓名和房间名称。 然后,在另一个名称不同但房间名称相同的浏览器中打开应用程序。 现在,您可以开始与自己聊天,您会注意到消息是实时收到的:

史鲁比小说App,已解锁永久会员,超多付费小说统统白嫖!

结论

在本文中,我们了解了 WebSockets、它的应用程序以及如何通过利用 Django Channels 在 Django 中使用它。 最后,我们介绍了如何使用 React 建立与 Django 服务器的 WebSocket 连接。

尽管我们构建了一个高效的实时聊天应用程序,但您仍然可以进行改进。 例如,要存储消息,您可能会包含一个数据库连接。 作为本地后端的替代方案,您可以考虑使用 Redis 作为消息代理。

我希望您喜欢这篇文章,如果您有任何问题,请务必发表评论。 快乐编码!

全面了解生产 React 应用程序

调试 React 应用程序可能很困难,尤其是当用户遇到难以重现的问题时。 如果您对监控和跟踪 Redux 状态、自动显示 JavaScript 错误以及跟踪缓慢的网络请求和组件加载时间感兴趣,请 尝试 LogRocket 。

LogRocket 就像一个用于 Web 和移动应用程序的 DVR,几乎可以记录您的 React 应用程序上发生的所有事情。 无需猜测问题发生的原因,您可以汇总并报告问题发生时应用程序所处的状态。 LogRocket 还监控您的应用程序的性能,并使用客户端 CPU 负载、客户端内存使用情况等指标进行报告。

LogRocket Redux 中间件包为您的用户会话增加了一层额外的可见性。 LogRocket 记录来自 Redux 存储的所有操作和状态。

你可能感兴趣的:(django,react.js,python)