FastAPI 中间件完全指南:从原理到实战,掌控请求响应的全局钩子

发布时间:2026/6/5 20:31:13

FastAPI 中间件完全指南:从原理到实战,掌控请求响应的全局钩子 【学习记录】FastAPI 中间件完全指南从原理到实战掌控请求响应的全局钩子在 Web 开发中有很多需求需要全局统一处理比如记录每个请求的日志、验证用户身份、添加跨域响应头、监控接口耗时等。FastAPI 提供了中间件Middleware机制允许我们在请求到达路由函数之前和响应返回客户端之前插入自定义逻辑。本文从原理、执行顺序到 5 种实战场景全面解析 FastAPI 中间件的使用方法并提供完整代码和面试问答。 目录什么是中间件中间件的执行时机与洋葱模型典型使用场景与完整代码示例3.1 日志记录中间件3.2 身份认证中间件3.3 跨域处理CORS3.4 自定义响应头中间件3.5 性能监控中间件测试与启动面试官可能会问 推荐回答总结与最佳实践一、什么是中间件中间件Middleware是一个在请求到达路径操作函数之前、响应返回给客户端之前执行的函数。它可以对请求和响应进行全局的预处理或后处理比如日志记录身份验证添加响应头性能监控跨域处理CORS请求限流在 FastAPI 中中间件是一个接收request和call_next参数的异步函数request传入的请求对象Request类型。call_next将请求传递给下一个中间件或最终的路由处理函数。中间件必须返回响应通常是通过await call_next(request)获得响应后再修改或直接返回。基本结构app.middleware(http)asyncdefmy_middleware(request:Request,call_next):# 请求前处理例如记录开始时间responseawaitcall_next(request)# 传递给下一个中间件或路由# 响应后处理例如添加响应头returnresponse二、中间件的执行时机与洋葱模型2.1 洋葱模型中间件的执行顺序遵循经典的洋葱模型请求从最外层进入逐层向内直到路由函数响应从最内层逐层向外返回。每个中间件都有机会在请求前和响应后执行代码。2.2 添加顺序与执行顺序的关系假设我们按顺序添加两个中间件 A 和 B先add_middleware(A)再add_middleware(B)实际上 B 会包裹A。执行流程请求先进入B 的请求前代码。B 调用call_next进入A 的请求前代码。A 调用call_next进入路由函数。路由函数返回响应后先回到A 的响应后代码。再回到B 的响应后代码。结论后添加的中间件先执行请求前代码后执行响应后代码。2.3 图解路由函数中间件A (先添加)中间件B (后添加)Client路由函数中间件A (先添加)中间件B (后添加)Client请求call_next()call_next()响应返回响应最终响应三、典型使用场景与完整代码示例以下是一个集成了5 种常用中间件的完整 FastAPI 应用涵盖日志、认证、CORS、响应头、性能监控。3.1 日志记录中间件记录每个请求的方法、路径、状态码和耗时。importtimeimportloggingfromfastapiimportFastAPI,Request appFastAPI()logging.basicConfig(levellogging.INFO,format%(asctime)s - %(message)s)loggerlogging.getLogger(__name__)app.middleware(http)asyncdeflog_requests(request:Request,call_next):starttime.perf_counter()responseawaitcall_next(request)durationtime.perf_counter()-start logger.info(f{request.method}{request.url.path}- Status{response.status_code}-{duration:.4f}s)returnresponse3.2 身份认证中间件检查请求头中的Authorization: Bearer token对受保护路径进行验证。VALID_TOKENsecret-token-123app.middleware(http)asyncdefauth_middleware(request:Request,call_next):# 公开路径跳过验证ifrequest.url.pathin[/docs,/openapi.json,/,/login]:returnawaitcall_next(request)auth_headerrequest.headers.get(Authorization)ifnotauth_headerornotauth_header.startswith(Bearer ):returnJSONResponse(status_code401,content{detail:Missing or invalid token})tokenauth_header.split( )[1]iftoken!VALID_TOKEN:returnJSONResponse(status_code403,content{detail:Invalid token})# 将用户信息存入 request.state供路由使用request.state.userauthenticated_userreturnawaitcall_next(request)3.3 跨域处理CORSFastAPI 内置了CORSMiddleware直接添加即可。fromfastapi.middleware.corsimportCORSMiddleware app.add_middleware(CORSMiddleware,allow_origins[*],# 生产环境应限制具体域名allow_credentialsTrue,allow_methods[*],allow_headers[*],)3.4 自定义响应头中间件为所有响应添加自定义头如X-Processed-By: FastAPI。app.middleware(http)asyncdefadd_custom_header(request:Request,call_next):responseawaitcall_next(request)response.headers[X-Processed-By]FastAPIresponse.headers[X-API-Version]1.0returnresponse3.5 性能监控中间件记录耗时超过阈值的慢请求可上报监控系统。app.middleware(http)asyncdefperformance_monitor(request:Request,call_next):starttime.monotonic()responseawaitcall_next(request)elapsedtime.monotonic()-startifelapsed1.0:# 超过1秒记录警告logger.warning(fSlow request:{request.method}{request.url.path}took{elapsed:.2f}s)returnresponse3.6 整合所有中间件的完整代码importtimeimportloggingfromfastapiimportFastAPI,Requestfromfastapi.responsesimportJSONResponsefromfastapi.middleware.corsimportCORSMiddleware appFastAPI()# 日志配置logging.basicConfig(levellogging.INFO,format%(asctime)s - %(message)s)loggerlogging.getLogger(__name__)# ---------- 1. 日志中间件 ----------app.middleware(http)asyncdeflog_requests(request:Request,call_next):starttime.perf_counter()responseawaitcall_next(request)durationtime.perf_counter()-start logger.info(f{request.method}{request.url.path}- Status{response.status_code}-{duration:.4f}s)returnresponse# ---------- 2. 认证中间件 ----------VALID_TOKENsecret-token-123app.middleware(http)asyncdefauth_middleware(request:Request,call_next):ifrequest.url.pathin[/docs,/openapi.json,/,/login]:returnawaitcall_next(request)auth_headerrequest.headers.get(Authorization)ifnotauth_headerornotauth_header.startswith(Bearer ):returnJSONResponse(status_code401,content{detail:Missing or invalid token})tokenauth_header.split( )[1]iftoken!VALID_TOKEN:returnJSONResponse(status_code403,content{detail:Invalid token})request.state.userauthenticated_userreturnawaitcall_next(request)# ---------- 3. CORS 中间件 ----------app.add_middleware(CORSMiddleware,allow_origins[*],allow_credentialsTrue,allow_methods[*],allow_headers[*],)# ---------- 4. 自定义响应头中间件 ----------app.middleware(http)asyncdefadd_custom_header(request:Request,call_next):responseawaitcall_next(request)response.headers[X-Processed-By]FastAPIresponse.headers[X-API-Version]1.0returnresponse# ---------- 5. 性能监控中间件 ----------app.middleware(http)asyncdefperformance_monitor(request:Request,call_next):starttime.monotonic()responseawaitcall_next(request)elapsedtime.monotonic()-startifelapsed1.0:logger.warning(fSlow request:{request.method}{request.url.path}took{elapsed:.2f}s)returnresponse# ---------- 测试路由 ----------app.get(/)asyncdefroot():return{message:Hello World}app.get(/protected)asyncdefprotected(request:Request):return{message:fWelcome,{request.state.user}}四、测试与启动4.1 启动服务uvicorn main:app--reload4.2 测试端点# 根路径公开无需认证curlhttp://localhost:8000/# 受保护路径不带 Token → 401curlhttp://localhost:8000/protected# 受保护路径带正确 Token → 200curl-HAuthorization: Bearer secret-token-123http://localhost:8000/protected# 查看 Swagger 文档公开http://localhost:8000/docs4.3 查看日志输出控制台会显示类似2025-06-04 10:00:01,123 - GET / - Status 200 - 0.0023s 2025-06-04 10:00:05,456 - GET /protected - Status 401 - 0.0012s五、面试官可能会问 推荐回答Q1什么是中间件在 FastAPI 中如何实现答中间件是在请求处理链中插入的钩子函数可以全局地修改请求或响应。FastAPI 中使用app.middleware(http)装饰器定义异步函数接收request和call_next最后必须调用call_next(request)并返回响应。Q2中间件的执行顺序是怎样的为什么后添加的中间件先执行请求前代码答执行顺序遵循“洋葱模型”。后添加的中间件包裹先添加的因此请求进入时先执行后添加中间件的请求前代码然后层层向内最终到达路由响应时逆向返回。这样可以保证每个中间件都能访问到最原始的请求也能处理最终响应。Q3如何实现一个只对特定路径生效的中间件答在中间件内部根据request.url.path判断对公开路径直接return await call_next(request)跳过处理。例如认证中间件中排除/docs、/等路径。Q4中间件和依赖项Dependency有什么区别什么时候用中间件什么时候用依赖答中间件全局、对所有请求生效适合日志、跨域、认证等横切关注点。依赖项更细粒度可以在具体路由或路由组中复用适合数据库会话、当前用户对象等需要注入到路径函数的场景。选择需要全局拦截 → 中间件需要部分路由使用或注入参数 → 依赖项。Q5如何捕获中间件中的异常并返回自定义错误响应答在中间件内使用try...except包裹await call_next(request)捕获异常后返回自定义的JSONResponse或PlainTextResponse。也可以结合全局异常处理器统一处理。Q6FastAPI 的 CORSMiddleware 是如何工作的为什么需要它答浏览器同源策略限制跨域请求CORSMiddleware 通过在响应头中添加Access-Control-Allow-Origin等字段告诉浏览器允许跨域访问。FastAPI 提供了现成的CORSMiddleware只需添加并配置允许的源、方法、头即可。Q7中间件的性能影响如何如何优化答中间件会增加每个请求的微小开销。应避免在中间件中执行耗时操作如同步阻塞 I/O。可以使用异步库、缓存或根据路径过滤只对需要的路径执行。对于性能监控可采样或异步上报。Q8如何编写测试来验证中间件行为答使用from fastapi.testclient import TestClient发送请求检查响应状态码、响应头、日志输出等。例如测试认证中间件不传 Token 应返回 401传正确 Token 返回 200。fromfastapi.testclientimportTestClient clientTestClient(app)deftest_auth():responseclient.get(/protected)assertresponse.status_code401responseclient.get(/protected,headers{Authorization:Bearer secret-token-123})assertresponse.status_code200六、总结与最佳实践中间件类型核心作用注意事项日志记录记录请求信息、耗时避免记录敏感数据身份认证验证 Token拦截未授权请求公开路径需跳过CORS解决跨域问题生产环境限制allow_origins自定义响应头添加统一标识避免覆盖重要头性能监控发现慢请求阈值需根据业务调整最佳实践建议✅ 中间件应保持轻量避免阻塞。✅ 使用request.state在中间件与路由间传递数据。✅ 注意中间件的添加顺序尤其是认证和日志的顺序通常认证在前。✅ 对静态资源或健康检查路径可以提前跳过中间件以提升性能。✅ 为中间件编写单元测试确保逻辑正确。掌握中间件你就能以极低的成本为 FastAPI 应用添加强大的全局能力。希望本文能帮助你写出更健壮、更可维护的 Web 服务。

相关新闻