v3.7.0
Highlights
Dependency injection for route handlers. Register providers with @api.dependency() and declare them as view parameters by name — no marker imports required:
@api.dependency()
async def db():
session = await create_session()
yield session # injected into views
await session.close() # teardown after the response
@api.route("/users/{id:int}")
async def get_user(req, resp, *, id, db):
resp.media = await db.fetch_user(id)Providers can be sync/async functions or generators (post-yield code runs as teardown). Providers taking a parameter receive the current Request. Each dependency resolves at most once per request, and path parameters take precedence on name collisions.
Per-route rate limiting. Apply @limiter.limit beneath @api.route() to give a single endpoint its own budget.
WebSocket rejection. Closing the socket in a before_request(websocket=True) hook now skips the route handler entirely. Sync WebSocket hooks are also supported.
Fixes
{value:float}path convertor matched garbage like1a5(unescaped regex dot) and crashed with a 500 — now a 404- Literal route characters are regex-escaped, so
/file.jsonno longer matches/fileXjson - Unbounded memory growth in
BackgroundQueue— completed futures are pruned req.media("form")crashed when theContent-Typeheader was missing- Custom formats registered on
api.formatswere silently ignored — they now reach request parsing and response negotiation - Content negotiation returned an empty body for encode-incapable
Acceptformats — now falls through to JSON
Plus small per-request performance wins: cached Request.url/Request.params, and format registries are no longer rebuilt twice per request.
Full changelog: https://github.com/kennethreitz/responder/blob/main/CHANGELOG.md