question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Signals `http.middleware.before` and `http.middleware.after` are dispatched for _every_ middleware

See original GitHub issue

For a single request, the signals http.middleware.before and http.middleware.after are dispatched for every middle on the app.

Example app:

from sanic import Sanic
from sanic.response import html

app = Sanic(__name__)

@app.middleware("request")
def my_middleware1(request):
    print("Middleware 1 ran", flush=True)

@app.middleware("request")
def my_middleware2(request):
    print("Middleware 2 ran", flush=True)

@app.signal("http.middleware.before", condition={"attach_to": "request"})
def handle_mw_before(request, response=None):
    print("Before middleware", flush=True)

@app.signal("http.middleware.after", condition={"attach_to": "request"})
def handle_mw_after(request, response):
    print("After middleware", flush=True)

@app.get("/")
def index(request):
    return html("<p>hello world</p>")

if __name__ == "__main__":
    app.run(debug=True, auto_reload=False, access_log=False)

Output:

[2022-01-04 13:57:50 +1000] [47672] [INFO] Starting worker [47672]
[2022-01-04 13:57:54 +1000] [47672] [DEBUG] Dispatching signal: http.middleware.before
Before middleware
Middleware 1 ran
[2022-01-04 13:57:54 +1000] [47672] [DEBUG] Dispatching signal: http.middleware.after
After middleware
[2022-01-04 13:57:54 +1000] [47672] [DEBUG] Dispatching signal: http.middleware.before
Before middleware    #<--- Run again
Middleware 2 ran
[2022-01-04 13:57:54 +1000] [47672] [DEBUG] Dispatching signal: http.middleware.after
After middleware     #<--- Run again

Applicable section of code: https://github.com/sanic-org/sanic/blob/a7bc8b56bab01e066357e6dcc67c0dc9df864298/sanic/app.py#L1346-L1372

It appears this is done intentionally. I don’t know what the usefulness of running a handler for every middleware is, except for perhaps debug tracing or handler logger.

Maybe it makes sense to create two new signals like http.middleware.before_all and http.middleware.after_all, in a place like this:

await self.dispatch( 
    "http.middleware.before_all", 
    inline=True, 
    context={ 
        "request": request, 
        "response": None, 
    }, 
    condition={"attach_to": "request"}, 
)
response = None
if applicable_middleware and not request.request_middleware_started:
    request.request_middleware_started = True
    for middleware in applicable_middleware: 
         await self.dispatch( 
             "http.middleware.before", 
             inline=True, 
             context={ 
                 "request": request, 
                 "response": None, 
             }, 
             condition={"attach_to": "request"}, 
         ) 
      
         response = middleware(request) 
         if isawaitable(response): 
             response = await response 
      
         await self.dispatch( 
             "http.middleware.after", 
             inline=True, 
             context={ 
                 "request": request, 
                 "response": None, 
             }, 
             condition={"attach_to": "request"}, 
         ) 
      
         if response: 
             break
await self.dispatch( 
    "http.middleware.after_all", 
    inline=True, 
    context={ 
        "request": request, 
        "response": response, 
    }, 
    condition={"attach_to": "request"}, 
)
return response

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:9 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
ahopkinscommented, Jan 4, 2022

We should probably also include a lifecycle diagram in the docs with the flow of handlers, middleware, and signals.

0reactions
stale[bot]commented, Sep 21, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is incorrect, please respond with an update. Thank you for your contributions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What is the difference between signal and middleware in ...
Middleware is a framework of hooks into Django's request/response processing. It's a light, low-level “plugin” system for globally altering ...
Read more >
Making and Using HTTP Middleware
Making and using middleware in Go is fundamentally simple. We want to: ... I'll explain how. ... In this code we put our...
Read more >
Middlewares - Watermill
SignalsHandler ) // Router level middleware are executed for every message sent to the router router.AddMiddleware( // CorrelationID will copy the ...
Read more >
Using Next.js' middleware and Edge Functions
js, use middleware to intercept an HTTP request and process it before it reaches the route handler. So, in this post, we'll learn...
Read more >
Signals
You can attach them just like any other signal to an application or blueprint instance. ... http.middleware.before, request, response ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found