feat: add response cache middleware with poisoning-safe keys

This commit is contained in:
2026-07-20 07:10:42 -04:00
parent c3d5079d39
commit 6286086551
4 changed files with 434 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
"""Redis 缓存后端: 笨 KV(CacheBackend 契约);redis 是 optional extra。
降级不在这里做——本类忠实上抛异常,静默降级是 CacheMW 的职责(算法一份)。
"""
from __future__ import annotations
from typing import Any
try:
import redis.asyncio as aioredis
except ImportError as _exc: # pragma: no cover - 依赖缺失路径
aioredis = None
_IMPORT_ERROR = _exc
else:
_IMPORT_ERROR = None
class RedisCache:
"""基于 redis.asyncio 的 KV;value 为 JSON 字符串,TTL 由调用方传入。"""
def __init__(self, client: Any) -> None:
if aioredis is None:
raise ImportError(
"Redis 缓存后端需要 redis 包: pip install 'polygateway[redis]'"
) from _IMPORT_ERROR
self._client = client
@classmethod
def from_url(cls, url: str) -> RedisCache:
if aioredis is None:
raise ImportError(
"Redis 缓存后端需要 redis 包: pip install 'polygateway[redis]'"
) from _IMPORT_ERROR
return cls(aioredis.from_url(url, decode_responses=True))
async def get(self, key: str) -> str | None:
return await self._client.get(key)
async def set(self, key: str, value: str, ttl_s: int) -> None:
await self._client.set(key, value, ex=ttl_s)
async def aclose(self) -> None:
await self._client.aclose()