在Angular 2+客户端和Django后端之间打开WebSocket连接

时间:2019-10-25 19:07:01

标签: python django angular websocket django-channels

我正在尝试使用Django Channels从我的Angular 2+应用程序和Django后端打开websocket连接。 我完成了本教程:https://www.youtube.com/watch?v=RVH05S1qab8 并设法使所有与javascript部分内嵌在Django html模板中的代码一起工作。但是当我将前端聊天表单迁移到单独的angular 2应用程序时,我只是简单地打开了一个websocket连接,就遇到了问题。 字体和后端都在本地托管。

前端-聊天表单模板

this.fetch()

前端-聊天表单组件

response

Django后端项目设置

<div *ngFor="let message of thread.messages">
  <div>{{message.message}}</div>
</div>

<form #formData [formGroup]="form">
  <div class="form-group">
    <input formControlName="messageInput" #msgInput type="text" class="form-control" id="newMessage" placeholder="Type a message">
  </div>
  <button (click)="onSubmit($event)" type="submit" class="btn btn-primary">Send</button>
</form>
</div>

Django Websocket路由

  otherUser: User;
  me: User;
  threadId: number;
  thread: Thread;
  endpoint: string;
  socket: WebSocket;

  form = new FormGroup({
    messageInput: new FormControl("")
  });

  get messageInput() {
    return this.form.get('messageInput');
  }


  getThreadById(id: number) {
    this._chatService.getThreadById(id).subscribe(thread => {
      console.log("response: thread found", thread);
      this.thread = thread;
    },
      error => {
        console.log("error", error);
      });
  }

  getEndpoint() {
    let loc = window.location
    let wsStart = "ws://"
    if (loc.protocol == "https:") {
      wsStart = 'wss://'
    }
    this.endpoint = wsStart + loc.host + loc.pathname;
    return this.endpoint;
  }


  constructor(private _activatedRoute: ActivatedRoute) {
  }

  ngOnInit() {
    this._activatedRoute.params.subscribe(params => { this.threadId = params['id']; });
    if (this.threadId) {
      this.getThreadById(this.threadId);
    }
    this.getEndpoint();
    this.createSocket();
  }

  createSocket() {
    this.socket = new WebSocket(this.endpoint);
    console.log("endpoint ", this.endpoint);

    this.socket.onopen = (e) => {
      console.log("open", e);
    }

    this.socket.onmessage = (e) => {
      console.log("message", e);
      let chatDataMsg = JSON.parse(e.data);
      this.thread.messages.push(chatDataMsg.message);
    }

    this.socket.onerror = (e) => {
      console.log("error", e);
    }

    this.socket.onclose = (e) => {
      console.log("close", e);
    }

  }

  onSubmit($event) {
    let msgText = this.messageInput.value;
    let finalData = {
      'message': msgText
    };
    let chatMessage: ChatMessage = new ChatMessage(this.thread, this.me, new Date(), msgText);
    this.socket.send(JSON.stringify(finalData))
    this.form.reset();
  }

Django Project Urls

ASGI_APPLICATION = "joole.routing.application"

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
            #"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')]
        },
    },
}


ALLOWED_HOSTS = ['joole-api.herokuapp.com', '.herokuapp.com', '127.0.0.1']

Django WebSocket使用者

from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator
from chat.consumers import ChatConsumer

application = ProtocolTypeRouter({
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                [
                    url(r"^messages/(?P<id>[\w.@+-]+)/$", ChatConsumer)
                ]
            )
        )
    )
})

Django聊天模型

from django.urls import path, include
from django.conf import settings

urlpatterns = [
    path('messages/', include('chat.urls')),
]

Django Chat Urls

import asyncio
import json
from users.models import CustomUser, Employee
from channels.consumer import AsyncConsumer
from channels.db import database_sync_to_async
from .models import Thread, ChatMessage


class ChatConsumer(AsyncConsumer):

    async def websocket_connect(self, event):
        print("CONNECTED", event)

        thread_id = self.scope['url_route']['kwargs']['id']
        thread_obj = await self.get_thread(thread_id)
        self.thread_obj = thread_obj
        chat_room = f"thread_{thread_obj.id}"
        self.chat_room = chat_room
        await self.channel_layer.group_add(
            chat_room,
            self.channel_name
        )
        await self.send({
            "type": 'websocket.accept'
        })

    async def websocket_receive(self, event):
        print("receive", event)
        front_text = event.get('text', None)
        if front_text is not None:
            loaded_dict_data = json.loads(front_text)
            msg = loaded_dict_data.get('message')
            user = self.scope['user']
            username = 'default'
            if user.is_authenticated:
                username = user.email
            myResponse = {
                'message': msg,
                'username': user.email
            }
            await self.create_chat_message(user, msg)
            # broadcasts the message event to be sent
            await self.channel_layer.group_send(
                self.chat_room,
                {
                    "type": "chat_message",
                    "text": json.dumps(myResponse)
                }
            )

    async def chat_message(self, event):
        # send the actual message event
        print("message", event)
        await self.send({
            "type": "websocket.send",
            "text": event["text"]
        })

    async def websocket_disconnect(self, event):
        print("disconnected", event)

    @database_sync_to_async
    def get_thread(self, id):
        return Thread.objects.get(id=id)

    @database_sync_to_async
    def create_chat_message(self, me, msg):
        thread_obj = self.thread_obj
        return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)

预期产量 我期待根据消费者类中的websocket_connect方法在控制台上打印事件信息的“ CONNECTED”消息。类似地,从Angular 2+ chat.component.ts在浏览器的控制台中触发了带有事件的“打开”消息。

实际输出

我立即在浏览器控制台上收到以下警告消息:

与'ws:// localhost:4200 / sockjs-node / 344 / ux0z32ma / websocket'的WebSocket连接失败:在建立连接之前关闭WebSocket。

等待约2分钟后... Attached image shows what is automatically output on the console.

1 个答案:

答案 0 :(得分:0)

我可能错过了一些东西,因为我看不到Django配置,但是据我了解,您写道您正在分别运行前端服务器和后端服务器。您可以看到前端正在尝试建立与localhost:4200的连接。我相信那是没有意义的角度服务器,您应该将WebSocket指向Django应用,所以我认为您应该修改以下方法:

SERVER_URL = "localhost:8000"

getEndpoint() {
  let wsStart = "ws://"
  if (window.location.protocol == "https:") {
    wsStart = 'wss://'
  }
  this.endpoint = wsStart + SERVER_URL + window.location.pathname;
  return this.endpoint;
}

但是,如果您将两个应用程序都托管在Django上,并在端口4200上公开该端口不适用,尽管我非常确定这不是您的情况。