Files
responder/tutorial-middleware.html
2026-07-14 04:14:56 +00:00

386 lines
33 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Writing Middleware &#8212; responder 9.2.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=eb4102c5" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/sphinx-design.min.css?v=95c83b7e" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=e8b940be"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-tabs.js?v=f930bc37"></script>
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Migrating from Flask" href="tutorial-flask.html" />
<link rel="prev" title="WebSocket Tutorial" href="tutorial-websockets.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="writing-middleware">
<h1>Writing Middleware<a class="headerlink" href="#writing-middleware" title="Link to this heading"></a></h1>
<p>Middleware sits between the server and your route handlers, processing
every request and response that flows through your application. It’s the
right tool for cross-cutting concerns — things that apply to <em>all</em>
requests, not just specific routes.</p>
<p>Common middleware use cases:</p>
<ul class="simple">
<li><p>Request logging and timing</p></li>
<li><p>Authentication and authorization</p></li>
<li><p>Adding security headers</p></li>
<li><p>Request ID generation</p></li>
<li><p>Rate limiting</p></li>
<li><p>Response compression (built-in)</p></li>
</ul>
<section id="hooks-vs-middleware">
<h2>Hooks vs. Middleware<a class="headerlink" href="#hooks-vs-middleware" title="Link to this heading"></a></h2>
<p>Responder gives you two levels of request processing:</p>
<p><strong>Hooks</strong> (<code class="docutils literal notranslate"><span class="pre">before_request</span></code> / <code class="docutils literal notranslate"><span class="pre">after_request</span></code>) run inside Responder’s
routing layer. They receive Responder’s <code class="docutils literal notranslate"><span class="pre">req</span></code> and <code class="docutils literal notranslate"><span class="pre">resp</span></code> objects and
are the simplest way to add behavior:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="n">before_request</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">add_header</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Powered-By&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;Responder&quot;</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">after_request</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">log_request</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">req</span><span class="o">.</span><span class="n">method</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">req</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">path</span><span class="si">}</span><span class="s2"> -&gt; </span><span class="si">{</span><span class="n">resp</span><span class="o">.</span><span class="n">status_code</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p><code class="docutils literal notranslate"><span class="pre">req.method</span></code> is uppercase (<code class="docutils literal notranslate"><span class="pre">&quot;GET&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">&quot;POST&quot;</span></code>, …) and compares
case-sensitively, so compare against uppercase strings: <code class="docutils literal notranslate"><span class="pre">req.method</span> <span class="pre">==</span> <span class="pre">&quot;GET&quot;</span></code>.</p>
</div>
<p><strong>Middleware</strong> runs at the ASGI level, wrapping the entire application.
It’s more powerful but more complex — you work with raw ASGI scopes
instead of Responder objects. Use middleware when you need to process
requests <em>before</em> they reach Responder’s routing, or when you need to
integrate with Starlette middleware.</p>
</section>
<section id="function-middleware">
<h2>Function Middleware<a class="headerlink" href="#function-middleware" title="Link to this heading"></a></h2>
<p>The quickest way to write HTTP middleware is the <code class="docutils literal notranslate"><span class="pre">&#64;api.middleware(&quot;http&quot;)</span></code>
decorator — a plain function that receives the Starlette <code class="docutils literal notranslate"><span class="pre">Request</span></code> and a
<code class="docutils literal notranslate"><span class="pre">call_next</span></code> callable, no class boilerplate required:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">time</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">middleware</span><span class="p">(</span><span class="s2">&quot;http&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">add_timing</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">call_next</span><span class="p">):</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
<span class="n">response</span> <span class="o">=</span> <span class="k">await</span> <span class="n">call_next</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
<span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Response-Time&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">elapsed</span><span class="si">:</span><span class="s2">.4f</span><span class="si">}</span><span class="s2">s&quot;</span>
<span class="k">return</span> <span class="n">response</span>
</pre></div>
</div>
<p>Call <code class="docutils literal notranslate"><span class="pre">call_next(request)</span></code> to pass the request onward; the return value is a
Starlette <code class="docutils literal notranslate"><span class="pre">Response</span></code> you can modify (or replace — return your own response
without calling <code class="docutils literal notranslate"><span class="pre">call_next</span></code> to short-circuit).</p>
<p>Synchronous functions work too. They are offloaded to the threadpool like
sync views, and the <code class="docutils literal notranslate"><span class="pre">call_next</span></code> they receive is a plain blocking callable:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">middleware</span><span class="p">(</span><span class="s2">&quot;http&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">tag_response</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">call_next</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">call_next</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Tag&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;handled&quot;</span>
<span class="k">return</span> <span class="n">response</span>
</pre></div>
</div>
<p>Function middleware registers through the same stack as <code class="docutils literal notranslate"><span class="pre">add_middleware</span></code>,
so the two compose freely — see <a class="reference internal" href="#middleware-order">Middleware Order</a> for where it sits.
Non-HTTP traffic (WebSockets, lifespan) passes through untouched.</p>
</section>
<section id="using-starlette-middleware">
<h2>Using Starlette Middleware<a class="headerlink" href="#using-starlette-middleware" title="Link to this heading"></a></h2>
<p>Responder is built on Starlette, so any Starlette middleware works
out of the box:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.base</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseHTTPMiddleware</span>
<span class="k">class</span><span class="w"> </span><span class="nc">TimingMiddleware</span><span class="p">(</span><span class="n">BaseHTTPMiddleware</span><span class="p">):</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">dispatch</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">call_next</span><span class="p">):</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">time</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
<span class="n">response</span> <span class="o">=</span> <span class="k">await</span> <span class="n">call_next</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="n">duration</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
<span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Response-Time&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">duration</span><span class="si">:</span><span class="s2">.3f</span><span class="si">}</span><span class="s2">s&quot;</span>
<span class="k">return</span> <span class="n">response</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">TimingMiddleware</span><span class="p">)</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">dispatch</span></code> method receives a Starlette <code class="docutils literal notranslate"><span class="pre">Request</span></code> and a
<code class="docutils literal notranslate"><span class="pre">call_next</span></code> function. Call <code class="docutils literal notranslate"><span class="pre">call_next(request)</span></code> to pass the request
to the next middleware (or to your route handler). The return value is
a Starlette <code class="docutils literal notranslate"><span class="pre">Response</span></code> that you can modify before it’s sent.</p>
</section>
<section id="built-in-middleware">
<h2>Built-in Middleware<a class="headerlink" href="#built-in-middleware" title="Link to this heading"></a></h2>
<p>Every Responder app ships with a small stack wired up for you:</p>
<ul class="simple">
<li><p><strong>ServerErrorMiddleware</strong> — catches unhandled exceptions and renders a 500</p></li>
<li><p><strong>ExceptionMiddleware</strong> — routes <code class="docutils literal notranslate"><span class="pre">HTTPException</span></code>s and status codes to your handlers</p></li>
<li><p><strong>TrustedHostMiddleware</strong> — validates the <code class="docutils literal notranslate"><span class="pre">Host</span></code> header (<code class="docutils literal notranslate"><span class="pre">[&quot;*&quot;]</span></code> by default)</p></li>
<li><p><strong>GZipMiddleware</strong> — compresses responses larger than 500 bytes (on by default)</p></li>
</ul>
<p>A few more are wired in on demand, by constructor flag:</p>
<ul class="simple">
<li><p><strong>SessionMiddleware</strong> — signed cookie sessions, on unless you pass
<code class="docutils literal notranslate"><span class="pre">sessions=False</span></code>. Secure by default: the signing key never falls back to a
public default, cookies are <code class="docutils literal notranslate"><span class="pre">Secure</span></code> in production, and <code class="docutils literal notranslate"><span class="pre">req.session</span></code> /
<code class="docutils literal notranslate"><span class="pre">resp.session</span></code> raise <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code> when sessions are off. See
<a class="reference internal" href="guide-config.html"><span class="doc">Configuration</span></a> for the full story.</p></li>
<li><p><strong>CORSMiddleware</strong> — <code class="docutils literal notranslate"><span class="pre">responder.API(cors=True)</span></code></p></li>
<li><p><strong>HTTPSRedirectMiddleware</strong> — <code class="docutils literal notranslate"><span class="pre">responder.API(enable_hsts=True)</span></code></p></li>
<li><p><strong>RequestIDMiddleware</strong> — <code class="docutils literal notranslate"><span class="pre">responder.API(request_id=True)</span></code> adds an
<code class="docutils literal notranslate"><span class="pre">X-Request-ID</span></code> header to every response</p></li>
<li><p><strong>LoggingMiddleware</strong> — <code class="docutils literal notranslate"><span class="pre">responder.API(enable_logging=True)</span></code> for structured
per-request logging (it handles request IDs itself, superseding <code class="docutils literal notranslate"><span class="pre">request_id</span></code>)</p></li>
<li><p><strong>MetricsMiddleware</strong> — <code class="docutils literal notranslate"><span class="pre">responder.API(metrics_route=&quot;/metrics&quot;)</span></code> exposes
Prometheus metrics</p></li>
</ul>
<p>The observability options (request IDs, logging, metrics) are covered in
<a class="reference internal" href="tour.html"><span class="doc">Feature Tour</span></a>.</p>
</section>
<section id="adding-third-party-middleware">
<h2>Adding Third-Party Middleware<a class="headerlink" href="#adding-third-party-middleware" title="Link to this heading"></a></h2>
<p>Any ASGI middleware can be added with <code class="docutils literal notranslate"><span class="pre">api.add_middleware()</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">some_package</span><span class="w"> </span><span class="kn">import</span> <span class="n">SomeMiddleware</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">SomeMiddleware</span><span class="p">,</span> <span class="n">option1</span><span class="o">=</span><span class="s2">&quot;value&quot;</span><span class="p">,</span> <span class="n">option2</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>Keyword arguments are passed to the middleware’s constructor.</p>
<p>Middleware can be registered any time before the first request, not only at
construction — the ASGI stack is assembled lazily and rebuilt whenever you add
more. That assembled stack is exposed as the read-only <code class="docutils literal notranslate"><span class="pre">api.app</span></code> property, so
you can’t inject middleware by assigning to it. Use <code class="docutils literal notranslate"><span class="pre">api.add_middleware()</span></code>, or
wrap the API object itself for a truly outermost layer (see <a class="reference internal" href="#middleware-order">Middleware Order</a>).</p>
</section>
<section id="middleware-order">
<h2>Middleware Order<a class="headerlink" href="#middleware-order" title="Link to this heading"></a></h2>
<p>Middleware wraps your application like the layers of an onion. A request
travels inward through every layer to your route, and the response travels
back outward in reverse.</p>
<p>The full built-in stack, from outermost to innermost, is:</p>
<ol class="arabic simple">
<li><p><strong>LoggingMiddleware</strong> (<code class="docutils literal notranslate"><span class="pre">enable_logging=True</span></code>) <em>or</em> <strong>RequestIDMiddleware</strong>
(<code class="docutils literal notranslate"><span class="pre">request_id=True</span></code>) — the observability tier. It wraps everything below,
so even a rendered 500 carries its <code class="docutils literal notranslate"><span class="pre">X-Request-ID</span></code> and real status.</p></li>
<li><p><strong>MetricsMiddleware</strong> (<code class="docutils literal notranslate"><span class="pre">metrics_route=...</span></code>)</p></li>
<li><p><strong>ServerErrorMiddleware</strong> — the outermost <em>application</em> layer; it catches
errors from every middleware and route beneath it.</p></li>
<li><p><strong>your middleware</strong> (added with <code class="docutils literal notranslate"><span class="pre">add_middleware</span></code> or <code class="docutils literal notranslate"><span class="pre">&#64;api.middleware</span></code>)</p></li>
<li><p><strong>TrustedHostMiddleware</strong></p></li>
<li><p><strong>HTTPSRedirectMiddleware</strong> (<code class="docutils literal notranslate"><span class="pre">enable_hsts=True</span></code>)</p></li>
<li><p><strong>CORSMiddleware</strong> (<code class="docutils literal notranslate"><span class="pre">cors=True</span></code>)</p></li>
<li><p><strong>SessionMiddleware</strong> (unless <code class="docutils literal notranslate"><span class="pre">sessions=False</span></code>)</p></li>
<li><p><strong>GZipMiddleware</strong> (on by default)</p></li>
<li><p><strong>ExceptionMiddleware</strong> — routes non-500 exceptions to your handlers</p></li>
<li><p><strong>your routes</strong></p></li>
</ol>
<p>Two consequences worth knowing:</p>
<ul class="simple">
<li><p>Your middleware sits <em>inside</em> <code class="docutils literal notranslate"><span class="pre">ServerErrorMiddleware</span></code>, so an exception it
raises is caught and rendered as a 500 instead of crashing the server.</p></li>
<li><p>Sessions sit beneath <code class="docutils literal notranslate"><span class="pre">ServerErrorMiddleware</span></code>, so they are <em>not</em> persisted on
an unhandled 500.</p></li>
</ul>
<p><code class="docutils literal notranslate"><span class="pre">api.add_middleware()</span></code> inserts your middleware just inside
<code class="docutils literal notranslate"><span class="pre">ServerErrorMiddleware</span></code> — <em>not</em> at the very top of the stack. Among your own
middleware — function-style and class-based alike — the most-recently-added is
the outermost and runs first, so if middleware A depends on B having run
first, add B before A.</p>
<p>To wrap <em>everything</em> — including error rendering and the observability tier —
wrap the API object itself:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">asgi</span> <span class="o">=</span> <span class="n">MyOutermostMiddleware</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>
</pre></div>
</div>
<p>That <code class="docutils literal notranslate"><span class="pre">asgi</span></code> callable is what you then serve.</p>
</section>
<section id="writing-pure-asgi-middleware">
<h2>Writing Pure ASGI Middleware<a class="headerlink" href="#writing-pure-asgi-middleware" title="Link to this heading"></a></h2>
<p>For maximum performance and control, you can write middleware as a plain
ASGI application. This bypasses Starlette’s <code class="docutils literal notranslate"><span class="pre">BaseHTTPMiddleware</span></code>
abstraction — it’s faster and gives you direct access to the ASGI
protocol:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">SecurityHeadersMiddleware</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">app</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send</span><span class="p">):</span>
<span class="k">if</span> <span class="n">scope</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span> <span class="o">!=</span> <span class="s2">&quot;http&quot;</span><span class="p">:</span>
<span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">send_with_headers</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
<span class="k">if</span> <span class="n">message</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;http.response.start&quot;</span><span class="p">:</span>
<span class="n">extra</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="sa">b</span><span class="s2">&quot;x-content-type-options&quot;</span><span class="p">,</span> <span class="sa">b</span><span class="s2">&quot;nosniff&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="sa">b</span><span class="s2">&quot;x-frame-options&quot;</span><span class="p">,</span> <span class="sa">b</span><span class="s2">&quot;DENY&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="sa">b</span><span class="s2">&quot;referrer-policy&quot;</span><span class="p">,</span> <span class="sa">b</span><span class="s2">&quot;strict-origin-when-cross-origin&quot;</span><span class="p">),</span>
<span class="p">]</span>
<span class="n">message</span><span class="p">[</span><span class="s2">&quot;headers&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">message</span><span class="p">[</span><span class="s2">&quot;headers&quot;</span><span class="p">])</span> <span class="o">+</span> <span class="n">extra</span>
<span class="k">await</span> <span class="n">send</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
<span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send_with_headers</span><span class="p">)</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">SecurityHeadersMiddleware</span><span class="p">)</span>
</pre></div>
</div>
<p>This is the same pattern used internally by Starlette and uvicorn. The
middleware receives the ASGI <code class="docutils literal notranslate"><span class="pre">scope</span></code>, <code class="docutils literal notranslate"><span class="pre">receive</span></code>, and <code class="docutils literal notranslate"><span class="pre">send</span></code> callables,
and wraps <code class="docutils literal notranslate"><span class="pre">send</span></code> to inject headers into the response.</p>
<p>For most cases, <code class="docutils literal notranslate"><span class="pre">BaseHTTPMiddleware</span></code> is simpler and perfectly fine.
Use the pure ASGI approach when you need to handle WebSocket connections,
streaming responses, or want to avoid the overhead of request/response
object creation.</p>
</section>
<section id="when-to-use-what">
<h2>When to Use What<a class="headerlink" href="#when-to-use-what" title="Link to this heading"></a></h2>
<ul class="simple">
<li><p><strong>Simple header additions, logging, auth checks</strong> → use hooks</p></li>
<li><p><strong>Response transformation, timing, third-party integrations</strong> → use
middleware (start with <code class="docutils literal notranslate"><span class="pre">&#64;api.middleware(&quot;http&quot;)</span></code>)</p></li>
<li><p><strong>Rate limiting</strong> → use the built-in <code class="docutils literal notranslate"><span class="pre">RateLimiter</span></code> (it uses hooks internally)</p></li>
<li><p><strong>Request ID</strong> → use <code class="docutils literal notranslate"><span class="pre">api</span> <span class="pre">=</span> <span class="pre">responder.API(request_id=True)</span></code></p></li>
</ul>
<p>Start with hooks. They’re simpler and cover most cases. Graduate to
middleware when hooks aren’t enough.</p>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="index.html">
<img class="logo" src="_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v9.2.0</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul><h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">User Guide</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quick Start</a></li>
<li class="toctree-l1"><a class="reference internal" href="tour.html">Feature Tour</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="routers.html">Composing Apps with Routers</a></li>
<li class="toctree-l1"><a class="reference internal" href="guide-config.html">Configuration</a></li>
<li class="toctree-l1"><a class="reference internal" href="clientgen.html">Generated Clients</a></li>
<li class="toctree-l1"><a class="reference internal" href="deployment.html">Deployment</a></li>
<li class="toctree-l1"><a class="reference internal" href="testing.html">Testing</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Tutorials</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="tutorial-rest.html">Building a REST API</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-sqlalchemy.html">Using SQLAlchemy</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-auth.html">Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-websockets.html">WebSocket Tutorial</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Writing Middleware</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#hooks-vs-middleware">Hooks vs. Middleware</a></li>
<li class="toctree-l2"><a class="reference internal" href="#function-middleware">Function Middleware</a></li>
<li class="toctree-l2"><a class="reference internal" href="#using-starlette-middleware">Using Starlette Middleware</a></li>
<li class="toctree-l2"><a class="reference internal" href="#built-in-middleware">Built-in Middleware</a></li>
<li class="toctree-l2"><a class="reference internal" href="#adding-third-party-middleware">Adding Third-Party Middleware</a></li>
<li class="toctree-l2"><a class="reference internal" href="#middleware-order">Middleware Order</a></li>
<li class="toctree-l2"><a class="reference internal" href="#writing-pure-asgi-middleware">Writing Pure ASGI Middleware</a></li>
<li class="toctree-l2"><a class="reference internal" href="#when-to-use-what">When to Use What</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-flask.html">Migrating from Flask</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Reference</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="api.html">API Reference</a></li>
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command Line Interface</a></li>
<li class="toctree-l1"><a class="reference internal" href="runtime-contracts.html">Runtime Contracts</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Project</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="changes.html">Changelog</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v9.html">Migrating to v9</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v8.html">Migrating to v8</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v7.html">Migrating to v7</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v6.html">Migrating to v6</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v5.html">Migrating to v5</a></li>
<li class="toctree-l1"><a class="reference internal" href="sandbox.html">Sandbox</a></li>
<li class="toctree-l1"><a class="reference internal" href="backlog.html">Backlog</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
<li>Previous: <a href="tutorial-websockets.html" title="previous chapter">WebSocket Tutorial</a></li>
<li>Next: <a href="tutorial-flask.html" title="next chapter">Migrating from Flask</a></li>
</ul></li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/tutorial-middleware.rst.txt"
rel="nofollow">Page source</a>
</div>
<a href="https://github.com/kennethreitz/responder" class="github">
<img src="_static/forkme_right_darkblue.png" alt="Fork me on GitHub" class="github"/>
</a>
</body>
</html>