【实战指南】在Django-Ninja框架中实现WebSocket消息推送的全过程与代码示例

在Django-Ninja框架中实现消息推送全过程

随着Web应用的发展,实时通信变得越来越重要。消息推送技术允许服务器主动向客户端发送数据,而不是客户端不断地轮询服务器。在Django-Ninja框架中实现消息推送不仅可以提升用户体验,还可以增强应用的功能性。本文将详细介绍如何在Django-Ninja项目中实现消息推送,并提供一个完整的示例。

一、消息推送技术的选择

在实现消息推送之前,我们需要选择合适的技术栈。常用的几种技术包括WebSocket、Server-Sent Events (SSE) 和长轮询(Long Polling)等。考虑到WebSocket提供了双向通信的能力,并且已经成为Web实时通信的标准,本文将采用WebSocket作为消息推送的技术方案。

二、准备工作
## 步骤 1: 环境搭建

确保你有一个基本的Django-Ninja项目。如果没有,可以通过以下命令创建一个新的项目:

# 创建Django项目
django-admin startproject myproject
cd myproject

# 安装Django-Ninja
pip install django-ninja

接下来,我们将使用Channels库来实现WebSocket的支持。Channels是Django的一个扩展库,允许Django处理异步请求和WebSockets。

pip install channels daphne

修改myproject/settings.py,启用Channels:

INSTALLED_APPS = [
    ...
    'channels',
]

ASGI_APPLICATION = 'myproject.routing.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

不太明白可以查看另一篇文章:https://blog.csdn.net/tombosky/article/details/141861940

三、配置路由
## 步骤 2: 配置ASGI路由

我们需要在myproject/routing.py中配置WebSocket路由:

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import myapp.routing  # 导入应用级别的路由配置

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            myapp.routing.websocket_urlpatterns
        )
    ),
})

同时,在myapp/routing.py中定义具体的路由:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/socket-server/', consumers.MyConsumer.as_asgi()),
]
四、创建消费者
## 步骤 3: 创建WebSocket消费者

myapp/consumers.py中定义一个WebSocket消费者类:

from channels.generic.websocket import AsyncWebsocketConsumer
import json

class MyConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'test'

        # 加入room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # 离开room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # 接收来自WebSocket的消息
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # 向room group发送消息
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # 接收来自room group的消息
    async def chat_message(self, event):
        message = event['message']

        # 发送消息给WebSocket
        await self.send(text_data=json.dumps({
            'message': message
        }))
五、发送消息
## 步骤 4: 发送消息

我们可以在视图中调用group_send方法来发送消息给特定的房间组。首先,确保你已经在myproject/settings.py中配置了任务队列,比如RabbitMQ或Redis。

# 在settings.py中添加
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'

接下来,安装Celery和Redis:

pip install celery redis

然后,在myapp/tasks.py中定义一个发送消息的任务:

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

@shared_task
def send_message(message):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        'test',
        {
            'type': 'chat_message',
            'message': message,
        }
    )
六、集成API端点
## 步骤 5: 创建API端点

myapp/api.py中创建一个API端点来触发消息发送:

from ninja import Router
from ninja import Schema
from .tasks import send_message

router = Router()

class MessageIn(Schema):
    message: str

@router.post("/send-message/")
def send_websocket_message(request, payload: MessageIn):
    send_message.delay(payload.message)
    return {"success": True}
七、运行项目
## 步骤 6: 启动Django项目和Celery worker

首先启动Django项目:

python manage.py runserver

然后启动Celery worker:

celery -A myproject worker --loglevel=info

最后,启动ASGI服务器(使用daphne):

daphne myproject.asgi:application
八、前端实现
## 步骤 7: 前端连接WebSocket

在前端JavaScript代码中建立WebSocket连接,并监听消息:

var socket = new WebSocket(`ws://${window.location.host}/ws/socket-server/`);

socket.onopen = function(e) {
    console.log("Connection established!");
};

socket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    console.log(data.message);
};

socket.onclose = function(event) {
    console.log('Socket is closed.', event);
};
九、测试
## 步骤 8: 测试消息推送

通过API端点发送消息:

curl -X POST http://localhost:8000/send-message/ -H "Content-Type: application/json" -d '{"message":"Hello from Django!"}'

前端控制台应该接收到消息,并打印出来。

十、总结

本文详细介绍了如何在Django-Ninja框架中实现基于WebSocket的消息推送功能。通过配置Channels、定义WebSocket消费者、创建Celery任务以及集成API端点,我们成功实现了消息的实时传输。此外,还展示了如何从前端JavaScript代码中连接WebSocket,并监听服务器发送的消息。


完整代码示例

下面是完整的代码示例,包括settings.pyrouting.pyconsumers.pytasks.pyapi.py以及前端JavaScript代码。

# myproject/settings.py (部分配置)
INSTALLED_APPS = [
    ...
    'channels',
]

ASGI_APPLICATION = 'myproject.routing.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'

# myproject/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import myapp.routing

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            myapp.routing.websocket_urlpatterns
        )
    ),
})

# myapp/routing.py
from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/socket-server/', consumers.MyConsumer.as_asgi()),
]

# myapp/consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json

class MyConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'test'

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

        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    async def chat_message(self, event):
        message = event['message']

        await self.send(text_data=json.dumps({
            'message': message
        }))

# myapp/tasks.py
from celery import shared_task
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer

@shared_task
def send_message(message):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        'test',
        {
            'type': 'chat_message',
            'message': message,
        }
    )

# myapp/api.py
from ninja import Router
from ninja import Schema
from .tasks import send_message

router = Router()

class MessageIn(Schema):
    message: str

@router.post("/send-message/")
def send_websocket_message(request, payload: MessageIn):
    send_message.delay(payload.message)
    return {"success": True}

前端JavaScript代码:

var socket = new WebSocket(`ws://${window.location.host}/ws/socket-server/`);

socket.onopen = function(e) {
    console.log("Connection established!");
};

socket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    console.log(data.message);
};

socket.onclose = function(event) {
    console.log('Socket is closed.', event);
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coderabo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值