Django Channels WebSocket路由报No route found for path错误排查
问题根因
路由报错的直接原因是前后端WebSocket请求路径不匹配,另外还有两处隐藏问题会导致功能运行异常:
- 后端路由表仅注册了
/function_images/ws/progress、/analysis_generator/ws/progress两个带应用前缀的路径,但前端所有页面都固定请求/ws/progress/,该路径未在路由表中注册,自然匹配失败抛出No route found错误。 - 后端路由正则未加路径起止锚点,也没有兼容末尾斜杠规则,就算路径前缀正确,前端带末尾斜杠的请求也可能出现匹配异常。
- 你在
connect方法中直接执行带time.sleep的同步阻塞死循环,会占用Channels的工作线程,就算路由匹配成功,后续WebSocket连接也会卡死,甚至影响其他正常请求。
修正方案
1. 修正前端WebSocket请求路径
根据当前访问的页面自动拼接对应应用前缀的WS地址,不要写死统一路径:
const establishSocketConnection = () => { // 自动提取当前页面的应用前缀,比如在/function_images/页面下提取到的前缀为function_images const pathPrefix = window.location.pathname.split('/')[1]; const socket_url = `ws://${window.location.host}/${pathPrefix}/ws/progress/`; let socket = new WebSocket(socket_url); socket.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type != "progress") { return; } const progress = +data.message; console.log(`Progress: ${progress}`); updateProgressBar(progress); } socket.onclose = (event) => { console.log("Socket connection closed, close code:", event.code); } socket.onerror = (error) => { console.log("Socket error: ", error); } return socket; }
2. 修正后端路由正则规则
给路径加上起止锚点,兼容末尾斜杠写法,避免匹配歧义:
from django.urls import re_path from function_images.consumers import FunctionImagesProgressConsumer from analysis_generator.consumers import AnalysisProgressConsumer websocket_urlpatterns = [ re_path(r"^function_images/ws/progress/?$", FunctionImagesProgressConsumer.as_asgi()), re_path(r"^analysis_generator/ws/progress/?$", AnalysisProgressConsumer.as_asgi()), ]
正则中
^标记路径起始位置,$标记路径结束位置,/?表示路径末尾可以携带0个或1个斜杠,完美兼容前端带斜杠的请求写法。
3. 修复Consumer阻塞问题
将阻塞式的进度轮询逻辑放到独立守护线程中执行,避免卡住Channels的主工作线程:
import threading import time import json import re from channels.generic.websocket import WebsocketConsumer class ProgressConsumer(WebsocketConsumer): max_anims = None def connect(self): self.log_cache = "" print("Websocket connected.") if not self.max_anims: # 未自定义NotSetError的话直接用内置ValueError即可 raise ValueError("max_anims not set") self.room_group_name = "progress" self.accept() # 阻塞逻辑放到独立线程运行,设置daemon=True保证主进程退出时线程自动回收 threading.Thread(target=self.start_progress_update, daemon=True).start() def start_progress_update(self): cached_progress = 0 progress = 0 while progress != 100.0: time.sleep(1) updates = self.get_log_updates() if updates == "": continue if "Error" in updates: self.send(json.dumps({"error": updates})) self.clean_log() self.close(3000) return progress = self.calculate_progress(updates) if progress == cached_progress: continue self.send(json.dumps({ 'type': 'progress', 'message': progress, })) cached_progress = progress def disconnect(self, close_code): print("Websocket disconnected. Code: ", close_code) def get_log_updates(self) -> str: if self.log_cache == self.get_log_cache(): return "" s1 = set(self.log_cache.split("\n")) s2 = set(self.get_log_cache().split("\n")) self.log_cache = self.get_log_cache() return str(s1.symmetric_difference(s2)) def calculate_progress(self, difference: str) -> float: if not "Animation" in difference: return 0.0 animation_id = float(re.search("Animation (\d+)", difference).group(1)) return ((animation_id + 1) / self.max_anims) * 100.0 def clean_log(self): raise NotImplementedError("clean_log not implemented") def get_log_cache(self) -> str: raise NotImplementedError("get_log_cache not implemented")
两个子类消费者的代码不需要修改,保持原有逻辑即可。
所有修改完成后重启Django服务,清空浏览器缓存重新测试,WebSocket即可正常连接、推送进度。
内容的提问来源于stack exchange,提问作者DeepQuantum
相关产品推荐
相关产品推荐

