channels icon indicating copy to clipboard operation
channels copied to clipboard

URLRouter with path() does not work with get_asgi_application() as a route

Open dvf opened this issue 2 years ago • 2 comments

System

Python 3.11.0 / macOS Ventura 13.1

asgiref           3.6.0   ASGI specs, helper code, and adapters
async-timeout     4.0.2   Timeout context manager for asyncio programs
black             22.12.0 The uncompromising code formatter.
channels          4.0.0   Brings async, event-driven capabilities to Django 3.2 and up.
channels-redis    4.0.0   Redis-backed ASGI channel layer implementation
click             8.1.3   Composable command line interface toolkit
django            4.1.5   A high-level Python web framework that encourages rapid development and clean, pragmatic design.
gunicorn          20.1.0  WSGI HTTP Server for UNIX
h11               0.14.0  A pure-Python, bring-your-own-I/O implementation of HTTP/1.1
msgpack           1.0.4   MessagePack serializer
mypy-extensions   0.4.3   Experimental type system extensions for programs checked with the mypy typechecker.
pathspec          0.10.3  Utility library for gitignore style pattern matching of file paths.
platformdirs      2.6.2   A small Python package for determining appropriate platform-specific dirs, e.g. a "user data dir".
pydantic          1.10.4  Data validation and settings management using python type hints
redis             4.4.0   Python client for Redis database and key-value store
setuptools        65.6.3  Easily download, build, install, upgrade, and uninstall Python packages
sqlparse          0.4.3   A non-validating SQL parser.
typing-extensions 4.4.0   Backported and Experimental Type Hints for Python 3.7+
uvicorn           0.20.0  The lightning-fast ASGI server.

Bug

According to the documentation we should be able to do long-polling alongside regular Django views:

If you want to split HTTP handling between long-poll handlers and Django views, use a URLRouter using Django’s get_asgi_application() specified as the last entry with a match-everything pattern.

But it looks like path("", get_asgi_application()) does not route to regular Django views:

project/settings.py

...
ASGI_APPLICATION = "project.asgi.application"
ROOT_URLCONF = "project.urls"

project/asgi.py

import os

from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path

from my_app.consumers import SSEConsumer

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")


application = ProtocolTypeRouter(
    {
        "http": URLRouter(
            [
                path("sse/<str:user_id>/", SSEConsumer.as_asgi()),
                path("", get_asgi_application()),
            ]
        )
    }
)

project/urls.py

from django.contrib import admin
from django.http import HttpResponse
from django.urls import path


def test(request):
    return HttpResponse("ok")


urlpatterns = [
    path("test/", test),
    path("admin/", admin.site.urls),
]

Running:

uvicorn project.asgi:application

Accessing http://localhost:8000/test/ results in an Exception:

INFO:     127.0.0.1:52810 - "GET /test/ HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/dvf/Library/Caches/pypoetry/virtualenvs/project-e3eQwoEC-py3.11/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 407, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dvf/Library/Caches/pypoetry/virtualenvs/project-e3eQwoEC-py3.11/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dvf/Library/Caches/pypoetry/virtualenvs/project-e3eQwoEC-py3.11/lib/python3.11/site-packages/channels/routing.py", line 62, in __call__
    return await application(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dvf/Library/Caches/pypoetry/virtualenvs/project-e3eQwoEC-py3.11/lib/python3.11/site-packages/channels/routing.py", line 134, in __call__
    raise ValueError("No route found for path %r." % path)
ValueError: No route found for path 'test/'.

dvf avatar Jan 10 '23 18:01 dvf

I figured it out, using re_path("", get_asgi_application()) works, but path("", get_asgi_application()) does not.

dvf avatar Jan 10 '23 18:01 dvf

up

jp-sft avatar Dec 18 '23 09:12 jp-sft

middleware will help i think for this?

IronJam11 avatar Jan 17 '25 08:01 IronJam11

This is a duplicate of #1428. It's documented in the URLRouter docs:

Please note that URLRouter nesting will not work properly with path() routes if inner routers are wrapped by additional middleware.

carltongibson avatar Jan 17 '25 08:01 carltongibson

@IronJam11 https://verification-pagan86.github.io/FTwO4tcCLBFPijpQzMmdmbGIm8WkwbFiV8usXFTZZ1Ip1aIIMA/