mirror of
https://github.com/kennethreitz/responder.git
synced 2026-10-24 05:59:12 +00:00
386 lines
33 KiB
HTML
386 lines
33 KiB
HTML
<!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 — 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">"X-Powered-By"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Responder"</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">"</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"> -> </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">"</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">"GET"</span></code>, <code class="docutils literal notranslate"><span class="pre">"POST"</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">"GET"</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">@api.middleware("http")</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">"http"</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">"X-Response-Time"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</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"</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">"http"</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">"X-Tag"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"handled"</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">"X-Response-Time"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</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"</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">["*"]</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="/metrics")</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">"value"</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">@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">"type"</span><span class="p">]</span> <span class="o">!=</span> <span class="s2">"http"</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">"type"</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"http.response.start"</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">"x-content-type-options"</span><span class="p">,</span> <span class="sa">b</span><span class="s2">"nosniff"</span><span class="p">),</span>
|
||
<span class="p">(</span><span class="sa">b</span><span class="s2">"x-frame-options"</span><span class="p">,</span> <span class="sa">b</span><span class="s2">"DENY"</span><span class="p">),</span>
|
||
<span class="p">(</span><span class="sa">b</span><span class="s2">"referrer-policy"</span><span class="p">,</span> <span class="sa">b</span><span class="s2">"strict-origin-when-cross-origin"</span><span class="p">),</span>
|
||
<span class="p">]</span>
|
||
<span class="n">message</span><span class="p">[</span><span class="s2">"headers"</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">"headers"</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">@api.middleware("http")</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">
|
||
©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> |