Как правильно отправить ответ на React Frontend из Django Channels?
Я пытаюсь использовать Django Channels для реализации long-polling для React frontend веб-приложения и Django REST backend. Я считаю, что многое из того, что у меня есть, работает в той или иной степени, но некоторые вещи должны быть неправильно настроены или закодированы для получения неожиданных результатов.
Вкратце, проблема, которую я получаю, заключается в том, что когда Django Channels Consumer отправляет ответ, он не сразу возвращается во фронтенд (согласно console.log(...)
в коде); скорее, кажется, что Consumer должен отправить другой ответ, а затем предыдущий или оба ответа появляются во фронтенде.
Я пытаюсь реализовать с помощью AsyncHttpConsumer
, потому что Websockets невозможны в нашем сценарии использования.
Asgi.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter(
{
"http": URLRouter(
longpoll_urlpatterns + [re_path(r"", django_asgi_app)]
),
}
)
Routing.py
longpoll_urlpatterns = [
# Tried using re_path but did not work
path("analysisSubscription/<int:analysisId>/", consumers.AnalysisConsumer.as_asgi()),
]
Consumers.py
class AnalysisConsumer(AsyncHttpConsumer):
async def handle(self, body):
print("In Handle")
print(self.scope)
self.analysisId = self.scope["url_route"]["kwargs"]["analysisId"]
self.analysis_group_name = f"analysis_{self.analysisId}"
# Register with the appropriate channel
await self.channel_layer.group_add(self.analysis_group_name, self.channel_name)
await self.send_headers(headers=[
(b"Content-type", b"application/json"),
(b"Access-Control-Allow-Origin", b"*"),
(b"Access-Control-Allow-Headers", b"Origin, X-Requested-With, Content-Type, Accept, Authorization"),])
#The server won't send the headers unless we start sending the body
await self.send_body(b"", more_body=True)
print("Registered consumer for ID: ", self.analysisId, " and group: ", self.analysis_group_name)
# await self.channel_layer.group_send(self.analysis_group_name, {"type": "analysis.update", "text": self.analysisId})
async def http_request(self, message):
print("In Request")
print(message)
if "body" in message:
self.body.append(message["body"])
if not message.get("more_body"):
try:
await self.handle(b"".join(self.body))
except:
print("Stopping")
# If something goes wrong, disconnect
# In the parent this ALWAYS disconnects and thus long-polling breaks
await self.disconnect()
raise StopConsumer()
async def disconnect(self):
print("Disconnecting!")
await self.channel_layer.group_discard(self.analysis_group_name, self.channel_name)
async def analysis_update(self, event):
print(event)
print("Inside Analysis Consumer")
analysisId = event['id']
analysisData = ""
try:
analysisData = await self.getAnalysis(analysisId)
analysisData = json.dumps(analysisData)
except Exception as ex:
print("Failed to retrieve Analysis object: {ex}")
return
print("Retrieved analysis:\n\t", analysisData)
await self.send_body(analysisData.encode('utf-8'))
print("Sent the response")
await asyncio.sleep(1)
await self.http_disconnect(None)
@database_sync_to_async
def getAnalysis(self, id):
return AnalysisSerializer(Analysis.objects.filter(id=id)[0]).data
Затем в файле Views.py во время обновления анализа я вызываю следующую строку для связи с группой потребителей и вызова ранее определенной функции, analysis_update
.
async_to_sync(layers.group_send)(f"analysis_{idAnalysis}", {"type": "analysis.update", "id": idAnalysis})
Код фронтенда ReactJS зацикливается, в основном выполняя следующие действия и проверяя response
.
await axios.get(ApiUrl.analysisSubscribe(analysisId), {
timeout: 60000,
})
TL;DR; GET
запросы посылают запрос метода OPTIONS
перед GET
для получения информации.
После этого вопроса я пошел по альтернативному пути использования fetch
против Axios
для запросов. При отладке другой проблемы с задачей мы столкнулись с тем, что запрос fetch
на самом деле отправлял запрос метода OPTIONS
до того, как был отправлен запрос GET
.
Выяснилось, может ли это происходить и с Axios, но, похоже, что да (и это было подтверждено в ходе тестирования). Дополнительную информацию можно найти в разделе Аксиос меняет метод запроса на 'OPTIONS' вместо 'GET'.