Sử dụng WebSocket trong Django với Channels

Tổng quan:

Django trước phiên bản 3.0 sử dụng django-websocket để hỗ trợ WebSocket, tuy nhiên thư viện này đã bị ngừng hỗ trợ. Hiện tại, Django khuyến nghị sử dụng Channels để xây dựng ứng dụng thực thời gian thực.

Channels cho phép nâng cấp giao thức HTTP lên WebSocket, giúp trao đổi dữ liệu theo thời gian thực thay vì sử dụng phương pháp polling truyền thống. Framework này mở rộng Django để hỗ trợ cả HTTP và WebSocket.

Tài liệu chính thức: https://channels.readthedocs.io/en/stable/

Tạo project Django với tên webapp, cấu trúc thư mục:

project
    - webapp
        - __init__.py
        - settings.py
        - urls.py
        - wsgi.py
    - manage.py

Cài đặt thư viện:

Python mặc định chỉ hỗ trợ HTTP, để dùng WebSocket cần cài thêm:

pip install -U channels
'''
Phiên bản mới nhất sẽ cập nhật Django lên 3.2
Nếu cần giữ nguyên phiên bản Django 2.2+, sử dụng: pip install -U channels==2.3
'''
Django 2.2+ hỗ trợ ASGI tích hợp sẵn
Trên Windows cần cài đặt trình biên dịch C++ nếu gặp lỗi

Cấu hình:

Cập nhật settings.py:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    ...
    'channels',
)


ASGI_APPLICATION = 'webapp.routing.application'

'''
ASGI_APPLICATION xác định vị trí routing chính là file application trong routing.py
Định dạng: 'tên_project.tên_file:routing_variable'
'''

Tạo file routing.py

Tạo file routing.py cùng cấp với settings.py:

from channels.routing import ProtocolTypeRouter

application = ProtocolTypeRouter({
    # Sẽ bổ sung sau
})

Khởi động ứng dụng

'''
WSGI: chỉ hỗ trợ HTTP
ASGI: hỗ trợ HTTP, async và WebSocket
'''

Kết quả như hình minh họa khi cấu hình thành công

Triển khai

Giả sử đã tạo app tên chat và thêm vào INSTALLED_APPS, cấu trúc:

chat
    - migrations
        - __init__.py
    - __init__.py
    - admin.py
    - apps.py
    - models.py
    - tests.py
    - views.py

Định nghĩa URL:

from django.urls import path
from chat.views import chat_view

urlpatterns = [
    path('chat', chat_view, name='chat-path')
]

Xử lý view:

from django.shortcuts import render

def chat_view(request):
    return render(request, 'chat/chat_template.html')

Triển khai WebSocket với Channels

Tạo file consumers.py và routing.py trong app chat:
consumers.py: tương tự views.py, xử lý logic kết nối WebSocket
routing.py: ánh xạ URL đến consumer tương ứng

1. Cấu hình routing.py

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing

application = ProtocolTypeRouter({
    'websocket': AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_patterns
        )
    ),
})

'''
ProtocolTypeRouter: Hỗ trợ nhiều giao thức, hiện chỉ cần cấu hình WebSocket

AuthMiddlewareStack: Cho phép truy cập thông tin người dùng qua self.scope["user"]

URLRouter: Định tuyến đến các consumer cụ thể
chat/routing.py nội dung:
from django.urls import path
from chat.consumers import MessageHandler

websocket_patterns = [
    path('ws/chat/', MessageHandler),
]

2. Viết consumer

from channels.generic.websocket import WebsocketConsumer
import json

class MessageHandler(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, code):
        pass

    def receive(self, data):
        content = json.loads(data)
        response = f"Server nhận được: {content['message']}"

        self.send(json.dumps({
            'reply': response
        }))
        
Lớp consumer cơ bản xử lý kết nối, nhận và gửi dữ liệu
from channels.generic.websocket import WebsocketConsumer


class MessageHandler(WebsocketConsumer):
    def connect(self):
        """Xử lý khi client yêu cầu kết nối"""
        self.accept()


    def receive(self, data):
        """Xử lý dữ liệu từ client"""
        pass


    def disconnect(self, close_code):
        """Xử lý khi kết nối bị ngắt"""
        pass

3. Triển khai client Vue.js

<script>
export default {
  data() {
    return {
      connection: null
    }
  },
  mounted() {
    this.setupConnection()
  },
  beforeDestroy() {
    this.connection.close()
  },
  methods: {
    setupConnection() {
      const endpoint = "ws://localhost:8000";
      this.connection = new WebSocket(endpoint);
      
      this.connection.onopen = () => {
        this.sendMessage({ type: 'init' })
      };
      
      this.connection.onmessage = event => {
        const result = JSON.parse(event.data)
        console.log('Nhận được:', result)
      };
      
      this.connection.onerror = error => {
        console.error('Lỗi kết nối:', error)
        this.reconnect()
      };
    },
    
    sendMessage(payload) {
      this.connection.send(JSON.stringify(payload))
    },
    
    reconnect() {
      setTimeout(() => {
        this.setupConnection()
      }, 5000)
    }
  }
}
</script>

Cấu trúc cuối cùng của app chat:

chat
    - migrations
        - __init__.py
    - __init__.py
    - admin.py
    - apps.py
    - models.py
    - tests.py
    - views.py
    - routing.py
    - consumers.py

Thẻ: Django Channels WebSocket ASGI Vue.js

Đăng vào ngày 9 tháng 6 lúc 18:36