Runtime
Between booting and shutting down, your app spends its life handling requests. WebFluid wraps
each one in a context and gives you a handful of hooks to shape the request
on its way in and the response on its way out. This is the layer that makes
render, url_for and friends "just know" about the current request.
The request context
On every request the framework enters a FluidContext. From anywhere downstream
— a route, a service, an event handler — you can grab it with
FluidContext.current() to reach the active app and request without threading
them through every function call. It's the same pattern our generated routes use:
from webfluid.core.context import FluidContext
async def handle_request():
ctx = FluidContext.current()
user = ctx.request.session.get("user")
return await ctx.fluid.render("index.html", user=user)
Outside of a request there is no context, and FluidContext.current() raises a RuntimeError. The framework's own helpers handle this gracefully by falling back to defaults, and so should yours when code might run from, say, a scheduled job.
Shaping requests and responses
Three decorators let you step into the request flow, all running inside that context:
-
before_request— runs before the route. Return nothing to continue, or return a response to short-circuit the request entirely. -
after_request— receives the response and returns one (possibly a new one). Great for headers or post-processing. -
context_processor— returns a dict that is merged into everyrender, so shared template variables don't have to be passed by hand.
from webfluid.core.context import FluidContext
def register(app):
@app.before_request
def require_session():
ctx = FluidContext.current()
if ctx.request.url.path.startswith("/admin"):
if not ctx.request.session.get("user"):
from fastapi.responses import RedirectResponse
return RedirectResponse("/login")
@app.after_request
async def add_header(response):
response.headers["X-Powered-By"] = "WebFluid"
return response
@app.context_processor
def globals_():
return {"brand": "My Portal"}
This is exactly how the surface's processing layer works internally — it registers a context processor for the shared template variables, a before-request logger and an after-request hook that swaps in the styled error pages. Additives get their own scoped versions of all three, as we saw in the previous chapter.
Themes at runtime
With WF_THEMES enabled, theme selection is a request-time decision. You remember
a choice in the session and read the active one back when rendering:
from fastapi import Request
async def pick_theme(request: Request):
ctx = FluidContext.current()
ctx.fluid.set_theme(request, "ocean") # stored in the session
return await ctx.fluid.render("index.html")
Proxies and rate limiting
Two more runtime conveniences worth knowing. If PROXY_FIX is set, the
fluid.asgi_app your server runs is wrapped in a proxy-headers middleware so
client ips survive a reverse proxy. And the built-in
slowapi limiter is reachable through
fluid.limit for per-route limits on top of the global defaults:
@app.get("/expensive")
@app.limit("5/minute")
async def expensive(request: Request):
return {"ok": True}
Continue reading
From here you can continue straight with Logging.