diff --git a/sanic/app.py b/sanic/app.py index 979eeba0..8659bed2 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -894,9 +894,19 @@ class Sanic(BaseSanic, RunnerMixin, metaclass=TouchUpMeta): ) # Run response handler + await self.dispatch( + "http.handler.before", + inline=True, + context={"request": request}, + ) response = handler(request, **request.match_info) if isawaitable(response): response = await response + await self.dispatch( + "http.handler.after", + inline=True, + context={"request": request}, + ) if request.responded: if response is not None: diff --git a/sanic/signals.py b/sanic/signals.py index 80c6300b..d3a2fa75 100644 --- a/sanic/signals.py +++ b/sanic/signals.py @@ -30,6 +30,8 @@ class Event(Enum): HTTP_LIFECYCLE_RESPONSE = "http.lifecycle.response" HTTP_ROUTING_AFTER = "http.routing.after" HTTP_ROUTING_BEFORE = "http.routing.before" + HTTP_HANDLER_AFTER = "http.handler.after" + HTTP_HANDLER_BEFORE = "http.handler.before" HTTP_LIFECYCLE_SEND = "http.lifecycle.send" HTTP_MIDDLEWARE_AFTER = "http.middleware.after" HTTP_MIDDLEWARE_BEFORE = "http.middleware.before" @@ -53,6 +55,8 @@ RESERVED_NAMESPACES = { Event.HTTP_LIFECYCLE_RESPONSE.value, Event.HTTP_ROUTING_AFTER.value, Event.HTTP_ROUTING_BEFORE.value, + Event.HTTP_HANDLER_AFTER.value, + Event.HTTP_HANDLER_BEFORE.value, Event.HTTP_LIFECYCLE_SEND.value, Event.HTTP_MIDDLEWARE_AFTER.value, Event.HTTP_MIDDLEWARE_BEFORE.value, diff --git a/tests/test_asgi.py b/tests/test_asgi.py index 129bbe0b..e612e3fe 100644 --- a/tests/test_asgi.py +++ b/tests/test_asgi.py @@ -531,6 +531,8 @@ async def test_signals_triggered(app): "http.lifecycle.handle", "http.routing.before", "http.routing.after", + "http.handler.before", + "http.handler.after", "http.lifecycle.response", # "http.lifecycle.send", # "http.lifecycle.complete", diff --git a/tests/test_handler.py b/tests/test_handler.py new file mode 100644 index 00000000..5267d29e --- /dev/null +++ b/tests/test_handler.py @@ -0,0 +1,36 @@ +from sanic.app import Sanic +from sanic.response import empty +from sanic.signals import Event + + +def test_handler_operation_order(app: Sanic): + operations = [] + + @app.on_request + async def on_request(_): + nonlocal operations + operations.append(1) + + @app.on_response + async def on_response(*_): + nonlocal operations + operations.append(5) + + @app.get("/") + async def handler(_): + nonlocal operations + operations.append(3) + return empty() + + @app.signal(Event.HTTP_HANDLER_BEFORE) + async def handler_before(**_): + nonlocal operations + operations.append(2) + + @app.signal(Event.HTTP_HANDLER_AFTER) + async def handler_after(**_): + nonlocal operations + operations.append(4) + + app.test_client.get("/") + assert operations == [1, 2, 3, 4, 5]