最近のDjangoプロジェクトの要件の1つは、ユーザーがメールの作成を終了してユーザーのCCを選択すると、ユーザーがメールを送信した後、サーバーがすべてのCCユーザーにプロンプトをアクティブに送信することです。ここでは、WebSoketプロトコルをサポートするチャネルを使用できます。
アイデアの簡単な説明:
- ユーザーがログインすると、ブラウザーが主導権を握ってWebScoket接続を送信します。サーバーが接続を受信すると、ユーザーIDで識別されるグループにユーザーを追加し、接続を受け入れるメッセージを返します。
- メールを送信するときは、CCユーザーのIDをトラバースし、IDに従って一度に対応するグループにメッセージを送信します。
ブラウザに受信情報がないことを確認するには、最初に簡単なhtmlページを作成します。受信した情報を表示するには:
<!-- static/templates/notificationMsg.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="notificationMsg-log" cols="100" rows="20"></textarea><br/>
</body>
<script>
var userID = {
{
userID }};
var notificationMsgSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/notifications/' + userID + '/'); // 客户端发起WebSocket,并返回WebSocket对象命名为notificationMsgSocket
notificationMsgSocket.onmessage = function(e) {
// 监听服务器发回的消息
var data = JSON.parse(e.data); // 将消息中data的值转为JS对象
var message = data['message']; // 获取其中message的值
alert(message)
document.querySelector('#notificationMsg-log').value += (message + '\n'); // 将message的值添加到notificationMsg-log中并换行
};
notificationMsgSocket.onclose = function(e) {
// WebSocket的readyState变为CLOSED时调用
console.error('Notification socket closed unexpectedly'); // 在控制台输出错误信息
};
</script>
</html>
このインターフェイスは、チャネルドキュメントに示されている例に基づいて簡略化されています。ページには、サーバーから送信されたメッセージを受信したときに表示されるテキストボックスが1つだけあります。
消費者、イベント
チャネルでは、コンシューマーはWebSocketでイベントを処理するために使用されます。コンシューマーは、Djangoのビューとして理解でき、イベントはHTTPのリクエストと見なすことができます。
ASGIでは、イベントは辞書の形式でサーバーに配信され、特定のイベントタイプは「type」キーでマークされます。異なるイベントは同じ名前のコンシューマーによって処理されます。たとえば、このプロジェクトでは、毎回「type」は「get_notification」イベントに送信されます。イベントを受け入れて応答するには、「get_notification」という名前のコンシューマーを定義する必要があります。
ビューで電子メール通知を送信します。
13 from asgiref.sync import async_to_sync
14 from channels.layers import get_channel_layer
...
31 class IssuedReportsView(View):
...
110 def post(self, request):
...
160 ¦ send_message_users = set(copy_to_user+supervise_user) # 将抄送人与审阅人的id放到同一个集合中
161 ¦ channel_layer = get_channel_layer() # 实例化get_channel_layer()对象,用于向组群发送消息
162 ¦ for user in send_message_users: # 遍历需要发送消息的用户id
163 ¦ ¦ async_to_sync(channel_layer.group_send)( # ASGI是异步的,这里转为同步操作;通过通信层向组群发送消息
164 ¦ ¦ ¦ "notification_"+str(user), # 用户所在的组群
165 ¦ ¦ ¦ {
166 ¦ ¦ ¦ 'type':'get_notification', # 标记发送事件的type
167 ¦ ¦ ¦ 'message': 'you have got a new report', # 提示信息
168 ¦ ¦ ¦ }
169 ¦ ¦ )
チャネルのルーティングを定義する
チャネルにはルートルートが必要です。公式の推奨事項は、構成ディレクトリのsettings.pyと同じパスに配置することです。チャネルは、さまざまなルーティングファイルに接続するためのさまざまなプロトコルを割り当てるために使用されます。ここではWebSocketファイルのみが処理されます。
1 # myproject/routing.py
2 from channels.routing import ProtocolTypeRouter, URLRouter
3 from channels.auth import AuthMiddlewareStack
4 import consumer.routing
5
6
7 application = ProtocolTypeRouter({
# 协议路由
8 # (http->django views is added by default)
9 'websocket': AuthMiddlewareStack( # 分发WebSocket连接,使用channels中的认证中间件,根据Django赋予request的session判断用户身份
10 ¦ URLRouter(
11 ¦ ¦ consumer.routing.websocket_urlpatterns # 根据consumer目录下的routing.py中的websocket_urlpatterns进一步分发websocket请求
12 ¦ )
13 ),
14 })
ルートルーティングでポイントされるルーティングファイルは次のとおりです。
1 # myproject/consumer/routing.py
2
3 from django.urls import re_path
4 from channels.auth import AuthMiddlewareStack
5 from consumer.Consumers import Consumers
6
7
8 websocket_urlpatterns = [
9 re_path(r'ws/notifications/(?P<userID>\w+)/$', Consumers), # 使用django的re_path方法,将匹配到的path交给不同的消费者处理
10 ]
登録済み
settings.pyに登録します。
# 首先在INSTALLED_APPS中注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'report',
'channels', # 注册channels
]
...
# 指定根路由文件,和下面的通信层一起放在文件尾部即可
ASGI_APPLICATION = 'starxteam.routing.application'
# 消费者中用到了通信层\channels.layer用于跨应用传递数据
# 但是需要使用第三方应用支持这个功能,官方推荐channels_redis
# 安装channels时默认安装了channels_redis
CHANNEL_LAYERS = {
'default':{
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
POSTMANを使用してテストします。
フロントエンドがメッセージを受信しました。