Как сделать вебсокеты в drf-проекте с реакт-фронтендом?
Всем привет! Как работать с вебсокетами в разделённом (бэк/фронт) проекте?
В фул джанго проекте делал так (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) // тут делаем что-то с данными })
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для использования вебсокетов в проекте 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), ]) ) ), })
# 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!');
Это пример базовой настройки вебсокетов в DRF и React. Не забудьте адаптировать код под ваш проект и добавить дополнительные функции, такие как проверка подлинности и авторизация.