You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Channels部署IIS后WebSocket路由找不到:1006错误排查

Django Channels部署IIS后WebSocket无法连接问题

问题现象

  • 本地通过python runserver运行时,WebSocket连接完全正常:
    new WebSocket('ws://127.0.0.1:8000/ws/notificacoes') // 成功建立连接
    
    后端日志显示:
    WebSocket HANDSHAKING /ws/notificacoes [127.0.0.1:53541]
    WebSocket CONNECT /ws/notificacoes [127.0.0.1:53541]
    
  • 通过IIS访问时,WebSocket连接直接失败:
    new WebSocket('ws://server:8000/ws/notificacoes') // 触发错误1006
    
    后端日志报错:ValueError: No route found for path 'ws/notificacoes'

相关代码文件

consumers.py

class NotificacaoConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'notificacoes'

        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    async def notificacao_update(self, event):
        await self.send(text_data=json.dumps({
            'message': 'Notificação atualizada!'
        }))

routing.py

websocket_urlpatterns = [
    ...
    path('ws/notificacoes', NotificacaoConsumer.as_asgi(), name='notificacao'),
]

asgi.py

from index.routing import websocket_urlpatterns
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings")
django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
        ),
    }
)

IIS反向代理配置(web.config)

Daphne运行在8002端口,当前配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appSettings>
        <add key="WSGI_HANDLER" value="django.core.wsgi.get_wsgi_application()" />
        <add key="PYTHONPATH" value="" />
        <add key="DJANGO_SETTINGS_MODULE" value="web.settings" />
        <add key="WSGI_LOG" value="logs\wfastcgi.log" />
    </appSettings>
    <system.webServer>
    <httpErrors errorMode="Detailed" />
    <handlers>
        <remove name="WebDAV" />
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <add name="Django Handler" path="*" verb="*" modules="FastCgiModule" scriptProcessor="C:\Python\python.exe|C:\Python\Lib\site-packages\wfastcgi.py" resourceType="Unspecified" requireAccess="Script" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,DELETE" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
    </handlers>
        <modules>
            <remove name="WebDAVModule" />
        </modules>
        <tracing>
            <traceFailedRequests>
                <add path="*">
                    <traceAreas>
                        <add provider="ASP" verbosity="Verbose" />
                        <add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
                        <add provider="ISAPI Extension" verbosity="Verbose" />
                        <add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,FastCGI,WebSocket" verbosity="Verbose" />
                    </traceAreas>
                    <failureDefinitions statusCodes="500" verbosity="Warning" />
                </add>
            </traceFailedRequests>
        </tracing>
        <httpProtocol>
            <customHeaders>
                <add name="Access-Control-Allow-Origin" value="*" />
            </customHeaders>
        </httpProtocol>
        <rewrite>
            <rules>
                <!-- Reverse proxy for Daphne running on 8002 -->
                <rule name="WebSocket Proxy" patternSyntax="Wildcard" stopProcessing="true">
                    <match url="ws*" />
                    <conditions>
                        <add input="{HTTP_CONNECT}" pattern="*" />
                    </conditions>
                    <action type="Rewrite" url="http://localhost:8002/{R:0}" logRewrittenUrl="true" />
                    <serverVariables>
                        <set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" />
                    </serverVariables>
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

解决方案

1. 修复反向代理规则的匹配逻辑

当前规则里的<add input="{HTTP_CONNECT}" pattern="*" />仅匹配CONNECT请求,但WebSocket握手使用的是GET请求,导致规则完全不触发——WebSocket请求被WSGI接管,自然找不到对应路由。

修改web.config中的反向代理规则:

<rule name="WebSocket Proxy" stopProcessing="true">
    <match url="ws/(.*)" />
    <conditions>
        <add input="{HTTP_UPGRADE}" pattern="websocket" />
        <add input="{HTTP_CONNECTION}" pattern="upgrade" />
    </conditions>
    <action type="Rewrite" url="http://localhost:8002/{R:0}" logRewrittenUrl="true" />
    <serverVariables>
        <set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" />
    </serverVariables>
</rule>
  • 用HTTP_UPGRADE和HTTP_CONNECTION这两个标准头识别WebSocket握手请求
  • 匹配URL改为ws/(.*),更精准锁定WebSocket路径

2. 确保Daphne正确监听

启动Daphne时绑定到0.0.0.0(允许所有IP访问),而非仅本地:

daphne -b 0.0.0.0 -p 8002 web.asgi:application

3. 统一路径的斜杠规则

routing.py中添加路径末尾的斜杠:

path('ws/notificacoes/', NotificacaoConsumer.as_asgi(), name='notificacao'),

前端连接同步改为:

new WebSocket('ws://server:8000/ws/notificacoes/')

避免Django路径匹配的歧义问题

4. 启用IIS的WebSocket组件

打开服务器管理器 -> 添加角色和功能 -> Web服务器(IIS) -> 应用程序开发 -> 勾选WebSocket协议,安装后重启IIS

5. 检查AllowedHosts配置

确保settings.py的ALLOWED_HOSTS包含服务器域名/IP:

ALLOWED_HOSTS = ['server', 'localhost', '127.0.0.1']

否则AllowedHostsOriginValidator会直接拒绝连接

验证步骤

  1. 重启IIS和Daphne服务
  2. 发起WebSocket连接,查看Daphne日志是否出现HANDSHAKING和CONNECT记录
  3. 若仍报错,查看IIS失败请求日志,确认请求是否正确转发到Daphne

内容的提问来源于stack exchange,提问作者Gustavo Lisi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 16:40:55