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))
错误原因总结
- 中间件注册方式错误:将中间件实例传给
add_middleware,而非类。FastAPI会自动将app实例传入中间件类的__init__方法,手动实例化导致参数传递混乱。 __call__方法不符合ASGI规范:类中间件必须遵循ASGI接口,接收scope、receive、send三个参数,而非request和call_next。- RequestCounter逻辑错误:字典层级判断错误,导致无法正确统计每个客户端的请求次数。
内容的提问来源于stack exchange,提问作者Abiodun Bankole
相关产品推荐
相关产品推荐

