You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FastAPI中实现请求缓存?附代码示例

FastAPI 请求缓存实现方案

根据你的代码场景,分接口类型给出针对性的缓存方案,同时修正原代码中的SQL注入风险问题:

一、GET 首页接口的缓存

这个接口返回HTML模板,适合从客户端HTTP缓存和服务器端缓存两方面优化:

1. 客户端HTTP缓存(推荐)

通过设置响应头让浏览器缓存页面,减少重复请求:

from fastapi import Response

@app.get("/")
def home(request: Request, response: Response):
    # 设置公共缓存,10分钟(600秒)过期
    response.headers["Cache-Control"] = "public, max-age=600"
    # 可选:添加ETag实现协商缓存,内容未变化时返回304
    response.headers["ETag"] = "home-page-v1"
    return templates.TemplateResponse("index.html", {"request": request})
  • Cache-Control:告诉浏览器缓存该响应,max-age指定缓存时长;
  • ETag:当浏览器再次请求时会携带If-None-Match头,服务器对比ETag一致则返回304,节省带宽。

2. 服务器端内存缓存

如果页面内容动态生成但更新频率低,可用内存缓存减少模板渲染开销:

from cachetools import TTLCache

# 缓存配置:最多存100条,10分钟过期
page_cache = TTLCache(maxsize=100, ttl=600)

@app.get("/")
def home(request: Request):
    cache_key = "home_page"
    # 若页面含用户专属内容,需将用户标识加入cache_key(比如request.session.get("user_id"))
    if cache_key in page_cache:
        return page_cache[cache_key]
    
    response = templates.TemplateResponse("index.html", {"request": request})
    page_cache[cache_key] = response
    return response

二、POST /api/getData 接口的缓存处理

你的该接口是插入数据到数据库的非幂等操作,绝对不能缓存请求触发的数据库写入逻辑,但可缓存其响应结果(如果响应内容可预测):

1. 修正原代码的SQL注入风险(必须做)

原代码用字符串拼接SQL存在严重安全问题,改用参数化查询:

from datetime import datetime

@app.post("/api/getData")
async def getData(request: Request, databody = Body()):
    data = databody["data"]
    
    with connection.cursor() as cursor:
        # 参数化查询避免SQL注入
        cursor.execute(
            "INSERT INTO database (ip, useragent, datetime) VALUES (%s, %s, %s)",
            (request.headers["host"], request.headers["user-agent"], datetime.now())
        )
        connection.commit()  # 别忘了提交事务
    
    # 不要直接返回Request对象(无法序列化),返回需要的字段
    return {
        "req": {
            "host": request.headers["host"],
            "user_agent": request.headers["user-agent"]
        }
    }

2. 服务器端响应缓存

如果同一请求参数的响应内容固定,可用缓存避免重复生成响应:

from cachetools import TTLCache

response_cache = TTLCache(maxsize=1000, ttl=300)  # 5分钟过期

@app.post("/api/getData")
async def getData(request: Request, databody = Body()):
    data = databody["data"]
    # 生成唯一缓存key:结合请求参数和客户端标识
    cache_key = f"getData_{data}_{request.headers.get('host', '')}_{request.headers.get('user-agent', '')}"
    
    if cache_key in response_cache:
        return response_cache[cache_key]
    
    # 执行数据库插入(已修正SQL注入)
    with connection.cursor() as cursor:
        cursor.execute(
            "INSERT INTO database (ip, useragent, datetime) VALUES (%s, %s, %s)",
            (request.headers["host"], request.headers["user-agent"], datetime.now())
        )
        connection.commit()
    
    response_data = {
        "req": {
            "host": request.headers["host"],
            "user_agent": request.headers["user-agent"]
        }
    }
    response_cache[cache_key] = response_data
    return response_data

3. 前端配合缓存

前端可将响应存在localStorage,减少重复请求:

async function fetchData(data) {
    const cacheKey = `getData_${JSON.stringify(data)}`;
    const cached = localStorage.getItem(cacheKey);
    
    if (cached) {
        const { data: cachedData, expires } = JSON.parse(cached);
        if (Date.now() < expires) {
            renderData(cachedData);
            return;
        }
        localStorage.removeItem(cacheKey); // 清除过期缓存
    }
    
    const res = await fetch('/api/getData', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ data })
    });
    const responseData = await res.json();
    
    // 存入缓存,设置5分钟过期
    localStorage.setItem(cacheKey, JSON.stringify({
        data: responseData,
        expires: Date.now() + 5 * 60 * 1000
    }));
    
    renderData(responseData);
}

function renderData(data) {
    // 你的页面渲染逻辑
    console.log(data);
}

三、进阶:分布式缓存(多实例部署场景)

如果应用是多实例部署,内存缓存无法共享,可改用Redis作为分布式缓存:

from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
from redis import asyncio as aioredis

# 启动时初始化Redis
@app.on_event("startup")
async def startup():
    redis = aioredis.from_url("redis://localhost")
    FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")

# 给GET接口加缓存(10分钟过期)
@app.get("/")
@cache(expire=600)
def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

# 给POST接口加缓存(仅当操作幂等时使用,这里不建议,除非你的插入逻辑是幂等的)
@app.post("/api/getData")
@cache(expire=300)
async def getData(request: Request, databody = Body()):
    # ... 数据库操作和响应生成 ...
    return response_data

内容的提问来源于stack exchange,提问作者TASK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 23:01:03