Как сделать вебсокеты в drf-проекте с реакт-фронтендом?

Ссылка скопирована
1 ответ

Всем привет! Как работать с вебсокетами в разделённом (бэк/фронт) проекте?

В фул джанго проекте делал так (1-й ответ):
https://stackoverflow.com/questions/56913676/dynam...

Сейчас отдельно фронт на реакте и drf-бэк. Во фронте будет использоваться socketio. Как, в таком случае, правильно реализовать вебсокеты в drf-ке? Те же channels?

Думал, что можно как в ответе на стэке, просто js-часть будет делать теперь фронт на реакте, а не я в джанго-шаблонах. Но фронт сказал, что эндпоинты не нужны, чисто по событиям:
у него будет что-то типа soket.on("getTasks")
soket.on("addTask", task: {"новая задача"}
И я понятия не имею, как с этим работать

Дополнительно:

Ответы:

DRF это REST API, т.е. там архитектура построена на HTTP запросах, а в вебсокет их нет, получает что DRF тут боком и для вебсокетов он вообще не нужен. Правильно - вынести бизнес логику из DRF в отдельный слой, а в вебсокетах дергать только эту логику и ничего не знать про DRF

UPD: Так же такой вариант, если REST API уже реализован, то написать для вебсокетов отдельный прокси, лучше не на джанго, а на starlette (fastapp скорее тут будут излишен), aiohttp или другом async фреймворки, который умеет в вебсокет. Так вот, этот прокси и будет уже дергать эндпоинты твой DRF, реальными http запросам

  • а можно поподробнее? Не в плане того, что вебсокеты с DRF никак не соприкасаются, а в плане реализации в целом. Я просто даже хз, с чего начать это делать.

    В джанго-проекте делал вебсокеты на channels, подключал в шаблонах (как в stackoverflow и показано), и потом через джанго-сигналы отправлял инфу на сокет. А тут как быть, какие технологии использовать и т.д.?

  • Dbtzhv, можешь channels, а еще мысль пришла, ща допишу в ответе.
  • Everything_is_bad, спасибо!
    А как это обрабатывать вообще с фронта?
    soket.on("getTasks")
    soket.on("addTask", task: {"новая задача"}
  • Dbtzhv, там событийная модель, подписываешь на события, ответы отдельными события, хотя вроде у socketio была возможно сразу и ответ давать

Я в проде запускал отдельный экземпляр приложения для web socket. Channels отлично справляется со своей задачей. Документация отлично описана, да и много гайдов есть.
Как пример:
asgi.py

import os  from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator   os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') get_asgi_app = get_asgi_application()  from notification import routing  application = ProtocolTypeRouter({     "http": get_asgi_app,     "websocket": AllowedHostsOriginValidator(         URLRouter(             routing.websocket_urlpatterns         )     ), })

import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') get_asgi_app = get_asgi_application() from notification import routing application = ProtocolTypeRouter({ "http": get_asgi_app, "websocket": AllowedHostsOriginValidator( URLRouter( routing.websocket_urlpatterns ) ), })

routing.py

from django.urls import path from . import consumers  websocket_urlpatterns = [     path(r'wss/notification/<str:room_name>/', consumers.NotificationConsumer.as_asgi()), ]

from django.urls import path from . import consumers websocket_urlpatterns = [ path(r'wss/notification/<str:room_name>/', consumers.NotificationConsumer.as_asgi()), ]

consumers.py

import json  from channels.generic.websocket import AsyncWebsocketConsumer  from service.auth.auth import get_auth   class NotificationConsumer(AsyncWebsocketConsumer):     user = None      async def connect(self):         user = await get_auth(self.scope.get('query_string'))         self.room_name = self.scope['url_route']['kwargs']['room_name']         max_connections_allowed = 1         user_connections_key = f'user_connections_{user.id}'          current_connections = self.scope.get(user_connections_key, 0)          if current_connections >= max_connections_allowed:             await self.close()         else:             self.scope[user_connections_key] = current_connections + 1             await self.channel_layer.group_add(                 self.room_name,                 self.channel_name             )             if user.is_access_allowed():                 self.user = user                 await self.accept()      async def disconnect(self, close_code):         pass      async def receive(self, text_data):         await self.send_notification({'message': text_data})      async def send_notification(self, message):         message = message["message"]         await self.send(text_data=json.dumps({"message": message}))   def send_notification(username, message, type_notification, created_at, id_notification, options=None):     channel_layer = get_channel_layer()     async_to_sync(channel_layer.group_send)(         f'notification-{username}',         {'type': 'send.notification',          'message': {'message': message, 'type_notification': type_notification, 'id': id_notification,                      'created_at': created_at, 'notification_options': options}})

import json from channels.generic.websocket import AsyncWebsocketConsumer from service.auth.auth import get_auth class NotificationConsumer(AsyncWebsocketConsumer): user = None async def connect(self): user = await get_auth(self.scope.get('query_string')) self.room_name = self.scope['url_route']['kwargs']['room_name'] max_connections_allowed = 1 user_connections_key = f'user_connections_{user.id}' current_connections = self.scope.get(user_connections_key, 0) if current_connections >= max_connections_allowed: await self.close() else: self.scope[user_connections_key] = current_connections + 1 await self.channel_layer.group_add( self.room_name, self.channel_name ) if user.is_access_allowed(): self.user = user await self.accept() async def disconnect(self, close_code): pass async def receive(self, text_data): await self.send_notification({'message': text_data}) async def send_notification(self, message): message = message["message"] await self.send(text_data=json.dumps({"message": message})) def send_notification(username, message, type_notification, created_at, id_notification, options=None): channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( f'notification-{username}', {'type': 'send.notification', 'message': {'message': message, 'type_notification': type_notification, 'id': id_notification, 'created_at': created_at, 'notification_options': options}})

на фронте можно использовать нативные методы (образец из vue)

const socket = ref(null) socket.value = new WebSocket(       `ws://127.0.0.1:8000/wss/notification/notification-${user.username}/}`     )     socket.value.onopen = () => {       console.log("otkrit")     }     socket.value.onclose = () => {       console.log("zakrit")     }      socket.value.onerror = () => {       console.log("error")     }     socket.value.onmessage = (message) => {         const data = JSON.parse(message.data)         // тут делаем что-то с данными      })

const socket = ref(null) socket.value = new WebSocket( `ws://127.0.0.1:8000/wss/notification/notification-${user.username}/}` ) socket.value.onopen = () => { console.log("otkrit") } socket.value.onclose = () => { console.log("zakrit") } socket.value.onerror = () => { console.log("error") } socket.value.onmessage = (message) => { const data = JSON.parse(message.data) // тут делаем что-то с данными })

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Артём Dev Ответ

Для использования вебсокетов в проекте Django с использованием Django Rest Framework (DRF) и React на фронтенде, вам потребуется настроить несколько компонентов.

1. Настройка DRF для работы с вебсокетами:
- Установите библиотеку channels для Django: `pip install channels`.
- Создайте файл routing.py в вашем приложении Django и определите маршруты для вебсокетов.
- Создайте файл consumers.py, где определите логику обработки вебсокет-сообщений.
- Обновите настройки проекта settings.py, добавив 'channels' в INSTALLED_APPS и настройки CHANNEL_LAYERS.

2. Настройка React для работы с вебсокетами:
- Установите библиотеку websocket для React: `npm install websocket`.
- Создайте компонент WebSocket.js, который будет устанавливать соединение с вебсокет-сервером и обрабатывать входящие сообщения.
- В вашем компоненте React, установите соединение с вебсокет-сервером и отправляйте сообщения при необходимости.

3. Связь между DRF и React:
- Создайте API-эндпоинт в DRF для установления соединения с вебсокетом и отправки сообщений.
- В вашем React-компоненте, отправляйте запросы к этому эндпоинту для установления соединения с вебсокетом и отправки сообщений.

Пример кода для настройки вебсокетов в DRF и React:

# routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator
from django.urls import path
from myapp import consumers
 
application = ProtocolTypeRouter({
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter([
                path('ws/myapp/', consumers.MyConsumer),
            ])
        )
    ),
})

# routing.py from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from channels.security.websocket import AllowedHostsOriginValidator from django.urls import path from myapp import consumers application = ProtocolTypeRouter({ 'websocket': AllowedHostsOriginValidator( AuthMiddlewareStack( URLRouter([ path('ws/myapp/', consumers.MyConsumer), ]) ) ), })

# consumers.py
from channels.generic.websocket import WebsocketConsumer
 
class MyConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()
 
    def disconnect(self, close_code):
        pass
 
    def receive(self, text_data):
        self.send(text_data=json.dumps({
            'message': text_data
        }))

# consumers.py from channels.generic.websocket import WebsocketConsumer class MyConsumer(WebsocketConsumer): def connect(self): self.accept() def disconnect(self, close_code): pass def receive(self, text_data): self.send(text_data=json.dumps({ 'message': text_data }))

// WebSocket.js
import WebSocket from 'websocket';
 
const ws = new WebSocket('ws://localhost:8000/ws/myapp/');
 
ws.onopen = () => {
    console.log('Connected to WebSocket');
};
 
ws.onmessage = (event) => {
    console.log('Received message: ', event.data);
};
 
ws.send('Hello, WebSocket!');

// WebSocket.js import WebSocket from 'websocket'; const ws = new WebSocket('ws://localhost:8000/ws/myapp/'); ws.onopen = () => { console.log('Connected to WebSocket'); }; ws.onmessage = (event) => { console.log('Received message: ', event.data); }; ws.send('Hello, WebSocket!');

Это пример базовой настройки вебсокетов в DRF и React. Не забудьте адаптировать код под ваш проект и добавить дополнительные функции, такие как проверка подлинности и авторизация.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно