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

FastAPI限流中间件报错:POSTMAN测试触发500内部服务器错误

FastAPI自定义限流中间件500错误排查与解决

问题描述

为FastAPI接口实现自定义限流中间件后,使用POSTMAN测试时出现500内部服务器错误,报错信息为:

TypeError: RateLimiterMiddleware.__call__() got an unexpected keyword argument 'app'

解决方案

1. 修正中间件类的ASGI规范实现

FastAPI/Starlette的类中间件必须遵循ASGI接口规范,__call__方法需接收scope、receive、send三个参数,同时调整限流逻辑的响应方式:

修改middleware.py:

from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
from request_counter import RequestCounter
import time


class RateLimiterMiddleware:
    def __init__(self, app, max_requests: int, window_seconds: int, request_counter: RequestCounter):
        self.app = app
        self.request_counter = request_counter
        self.max_requests = max_requests
        self.window_seconds = window_seconds

    async def __call__(self, scope, receive, send):
        # 仅处理HTTP请求
        if scope["type"] == "http":
            request = Request(scope, receive)
            client_ip = request.client.host
            current_time = time.time()

            try:
                self.request_counter.check_rate_limit(
                    client_ip, current_time, self.window_seconds, self.max_requests
                )
                self.request_counter.increase_request_count(
                    client_ip, current_time, self.window_seconds
                )
            except HTTPException as e:
                # 直接返回限流响应
                response = JSONResponse(content={"detail": e.detail}, status_code=e.status_code)
                await response(scope, receive, send)
                return

        # 传递请求到下一个中间件或路由
        await self.app(scope, receive, send)

2. 修复RequestCounter的计数逻辑错误

原代码存在字典层级判断错误,导致无法正确统计单个客户端的请求次数:

修改request_counter.py:

from fastapi import HTTPException


class RequestCounter:
    def __init__(self):
        self.request_counts = {}

    def increase_request_count(self, client_ip, current_time, window_seconds):
        timestamp = int(current_time - window_seconds)

        if client_ip not in self.request_counts:
            self.request_counts[client_ip] = {}

        # 修正:检查时间戳是否属于当前客户端的计数字典
        if timestamp not in self.request_counts[client_ip]:
            self.request_counts[client_ip][timestamp] = 0

        self.request_counts[client_ip][timestamp] += 1

    def check_rate_limit(self, client_ip, current_time, window_seconds, max_requests):
        timestamp = int(current_time - window_seconds)

        if client_ip not in self.request_counts or timestamp not in self.request_counts[client_ip]:
            return 0

        request_count = self.request_counts[client_ip][timestamp]
        if request_count >= max_requests:
            raise HTTPException(status_code=429, detail="Too Many Requests")

        return request_count

3. 修正中间件注册方式

FastAPI的add_middleware方法需要传入中间件类而非实例,配置参数通过关键字传递给类的__init__方法:

修改main.py:

from config import init_firebase
from firebase_admin import firestore
from fastapi import FastAPI, HTTPException
from models import Battery
from typing import List
from middleware import RateLimiterMiddleware
from request_counter import RequestCounter


init_firebase()

app = FastAPI()

db = firestore.client()

request_counter = RequestCounter()
# 传入中间件类,并用关键字参数传递限流配置
app.add_middleware(RateLimiterMiddleware, 
                   max_requests=10, 
                   window_seconds=60, 
                   request_counter=request_counter)


@app.post("/battery/", response_model=Battery)
async def create_battery(battery: Battery):
    try:
        doc_ref = db.collection("batteries").document()
        doc_ref.set(battery.model_dump())
        return {**battery.model_dump(), "id": doc_ref.id}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/batteries/", response_model=List[Battery])
async def get_batteries():
    try:
        ref = db.collection("batteries")
        batteries = ref.stream()

        battery_list = []

        for battery in batteries:
            data = battery.to_dict()
            battery_list.append(data)
        return battery_list
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

错误原因总结

  1. 中间件注册方式错误:将中间件实例传给add_middleware,而非类。FastAPI会自动将app实例传入中间件类的__init__方法,手动实例化导致参数传递混乱。
  2. __call__方法不符合ASGI规范:类中间件必须遵循ASGI接口,接收scope、receive、send三个参数,而非request和call_next。
  3. RequestCounter逻辑错误:字典层级判断错误,导致无法正确统计每个客户端的请求次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:17:13