【Fastapi学习笔记(7)】—— Fastapi 中间件、前端跨域请求
FastAPI 中间件Middleware详解一、什么是中间件中间件是介于客户端请求和接口路由函数之间的一层通用逻辑请求到达接口之前、响应返回客户端之前都会经过它。简单流程客户端请求→中间件前置逻辑→ 接口函数执行业务 →中间件后置逻辑→响应返回客户端特点全局生效默认对项目所有路由/接口统一处理统一抽离把多个接口重复的逻辑抽出来不用每个接口重复写代码链式执行可注册多个中间件按注册顺序依次执行FastAPI 底层基于 Starlette中间件写法完全兼容 Starlette。二、基础语法 最简示例1. 标准异步中间件写法推荐FastAPI 主流fromfastapiimportFastAPI,Requestimporttime appFastAPI()# 注册中间件app.middleware(http)asyncdefcalc_cost_time(request:Request,call_next):# 1. 请求进入接口【之前】执行前置逻辑 start_timetime.time()print(请求进来了,request.url.path)# 执行后续逻辑调用真正的接口函数responseawaitcall_next(request)# 2. 接口执行完毕响应返回【之前】执行后置逻辑 costtime.time()-start_timeprint(f接口耗时:{cost:.4f}s)# 可以追加响应头response.headers[X-Process-Time]str(cost)returnresponse# 测试接口app.get(/hello)asyncdefhello():return{msg:Hello FastAPI}关键说明app.middleware(http)声明这是 HTTP 全局中间件call_next(request)放行请求执行后续中间件 接口逻辑必须调用顺序前置代码 →call_next→ 后置代码能拿到Request对象读请求头、请求参数、客户端IP、Response对象改响应头三、核心应用场景工作最常用场景 1统一统计接口耗时、性能监控上面示例就是典型用法全局统计每个接口执行耗时方便排查慢接口。额外追加X-Process-Time响应头前端/网关也能看到耗时。场景 2全局日志统一打印请求/响应日志不用在每个接口写日志中间件统一记录客户端IP、请求路径、请求方式、状态码。app.middleware(http)asyncdeflog_request(request:Request,call_next):client_iprequest.client.host methodrequest.method pathrequest.url.pathprint(f【请求】IP:{client_ip}方法:{method}路径:{path})responseawaitcall_next(request)print(f【响应】状态码:{response.status_code})returnresponse场景 3全局跨域CORSFastAPI 内置CORSMiddleware专门解决前端跨域请求项目必备。fromfastapi.middleware.corsimportCORSMiddleware appFastAPI()# 配置跨域app.add_middleware(CORSMiddleware,allow_origins[*],# 允许所有域名生产建议指定具体域名allow_credentialsTrue,allow_methods[*],allow_headers[*],)场景 4全局鉴权 / 登录拦截统一校验 Token、身份信息不用每个接口单独写鉴权代码。注意中间件是全局拦截可搭配白名单登录接口、文档接口放行。fromfastapiimportHTTPExceptionapp.middleware(http)asyncdefauth_middleware(request:Request,call_next):# 白名单不需要登录的接口white_list[/login,/docs,/redoc,/openapi.json]ifrequest.url.pathnotinwhite_list:# 从请求头拿 Tokentokenrequest.headers.get(X-Token)ifnottokenortoken!123456:raiseHTTPException(status_code401,detail未授权请登录)# 校验通过放行responseawaitcall_next(request)returnresponse场景 5全局限流、防刷结合 IP 计数器在中间件实现全局限流拒绝高频恶意请求。场景 6统一添加/修改响应头给所有接口统一追加自定义头、安全响应头如X-Content-Type-Options、Retry-After等。场景 7链路追踪全链路 Trace ID全局生成唯一Request-ID塞入请求头、响应头、日志微服务排查问题必备。importuuidapp.middleware(http)asyncdeftrace_id_middleware(request:Request,call_next):trace_idstr(uuid.uuid4())# 传给后续接口/日志request.state.trace_idtrace_id responseawaitcall_next(request)# 响应头返回追踪IDresponse.headers[X-Request-Id]trace_idreturnresponse场景 8请求/响应统一加密、解码所有接口统一做参数解密、响应数据加密业务接口只关心逻辑。四、多中间件执行顺序注册多个中间件时遵循先注册 → 先执行前置逻辑后注册 → 先执行后置逻辑示例fromfastapiimportFastAPI,Request appFastAPI()# 第一个注册的中间件 M1 app.middleware(http)asyncdefm1(request:Request,call_next):print( M1 前置 执行 )responseawaitcall_next(request)print( M1 后置 执行 )returnresponse# 第二个注册的中间件 M2 app.middleware(http)asyncdefm2(request:Request,call_next):print( M2 前置 执行 )responseawaitcall_next(request)print( M2 后置 执行 )returnresponse# 测试接口路由函数app.get(/test)asyncdeftest():print(----- 路由函数 业务逻辑执行 -----)return{msg:ok}运行后控制台输出顺序固定 M1 前置 执行 M2 前置 执行 ----- 路由函数 业务逻辑执行 ----- M2 后置 执行 M1 后置 执行 逐行解读先执行 M1 前置 → 再执行 M2 前置正序按注册先后走到 路由函数接口核心逻辑路由执行完毕开始走后置逻辑先执行 M2 后置 → 再执行 M1 后置✅ 这里就是按和注册相反的顺序执行五、中间件 vs 依赖项Depends区别高频面试/实战区分很多人混淆两者一张表分清特性中间件 Middleware依赖项 Depends作用范围全局所有接口默认生效局部哪个接口加哪个生效执行位置路由匹配之前执行路由匹配之后、函数调用前执行适合场景跨域、全局日志、全局限流、全链路追踪、全局鉴权单个/分组接口鉴权、参数校验、局部复用逻辑操作对象可直接操作原始Request、Response侧重函数参数、业务数据选型建议全项目统一规则 → 用中间件部分接口复用逻辑 → 用Depends 依赖六、常见注意事项必须调用await call_next(request)不调用 请求卡死接口永远不会执行。中间件尽量轻量化全局执行逻辑太复杂会拖慢所有接口性能。异步项目用异步中间件不要混用同步阻塞代码。全局鉴权一定要配置白名单登录接口、文档/docs、静态资源必须放行否则无法访问。中间件内可以抛出HTTPException正常返回错误响应。七、一句话总结FastAPI 中间件是全局统一拦截层在请求进接口前、响应返回前执行通用逻辑主要用来做跨域、全局日志、接口耗时统计、全局限流、统一鉴权、链路追踪、统一响应头。全局通用逻辑抽中间件局部接口复用抽 Depends。前端跨域请求1. 前端跨域请求是什么结合浏览器同源策略、场景、现象、原因一步步讲清楚再配示例和 FastAPI 对应处理。一、先搞懂同源策略浏览器安全规则同源策略是浏览器默认的安全机制用来防止恶意网站窃取数据、发起非法请求。什么是「同源」两个 URL 必须同时满足协议、域名、端口三者完全一致才算同源任意一个不一样就是跨域。判断三要素协议http/https域名主域名 子域名端口号80/8000/3000等注意浏览器中http://a.com:80和http://a.com视为同源80是 http 默认端口可省略。二、举例同源 vs 跨域假设当前页面地址http://localhost:3000/index.html前端页面请求目标地址是否跨域原因http://localhost:3000/api/xxx同源协议、域名、端口全相同http://localhost:8000/api/xxx跨域端口不同3000 ↔ 8000https://localhost:3000/api/xxx跨域协议不同http ↔ httpshttp://127.0.0.1:3000/api/xxx跨域域名不同localhost ↔ 127.0.0.1浏览器判定为不同域名http://api.xxx.com:3000/data跨域域名不同三、什么是「前端跨域请求」前端页面JS/Fetch/Axios在浏览器环境中发起了一个非同源的后端接口请求这个行为就叫跨域请求。典型开发场景你日常一定会遇到前端项目运行在http://localhost:3000Vue/React/静态页面FastAPI 后端运行在http://localhost:8000前端 JS 直接调用后端接口 →端口不一致触发跨域四、跨域会出现什么现象请求其实已经发到后端接口正常接收、执行业务逻辑浏览器拦截返回结果不让前端 JS 拿到响应数据控制台报经典错误Access to fetch at http://localhost:8000/xxx from origin http://localhost:3000 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource.翻译跨域策略拦截响应头缺少允许跨域的字段。补充后端收得到请求但结果回不到前端跨域限制只存在于浏览器Postman、curl、APP、小程序 不受同源策略限制。五、CORS 是什么上面报错里的CORS跨域资源共享Cross-Origin Resource Sharing它是一套HTTP 响应头规范作用后端主动在响应头声明「允许哪些前端域名跨域访问我」以此说服浏览器放行。简单理解浏览器不让跨域 → 后端配置 CORS 响应头 → 浏览器看到允许规则 → 正常互通。六、FastAPI 如何解决跨域实操代码FastAPI 内置CORSMiddleware中间件一行配置搞定也是你之前学过的中间件用法。1. 完整代码fromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware appFastAPI()# 配置跨域中间件app.add_middleware(CORSMiddleware,# 1. 允许的前端源域名/地址allow_origins[http://localhost:3000,# 前端地址精准配置生产推荐https://xxx.com],allow_credentialsTrue,# 允许携带 Cookie、认证头如 X-Tokenallow_methods[*],# 允许所有请求方法GET/POST/PUT/DELETEallow_headers[*],# 允许所有请求头)# 测试接口app.get(/hello)asyncdefhello():return{msg:跨域请求成功}2. 参数说明allow_origins写具体地址安全生产环境首选写[*]允许所有域名跨域开发调试用生产不推荐。allow_credentialsTrue前端需要传Cookie、Token等身份凭证时必须开启。allow_methods/allow_headers限制允许的请求方法、请求头[*]代表全部放行。七、两种跨域请求简单请求 预检请求了解即可1. 简单请求满足条件GET/POST/HEAD 无特殊请求头、仅普通表单/JSON。流程直接发起真实请求后端返回 CORS 头浏览器判断放行。2. 预检请求OPTIONS当请求带自定义头比如X-Token、或者非简单请求时浏览器先自动发一条OPTIONS预检请求询问后端「你允许我跨域吗」后端返回跨域规则浏览器确认允许后再发起真实业务请求。你之前用到X-Token自定义请求头时前端跨域一定会触发 OPTIONS 预检。八、总结精简记忆同源策略浏览器安全规则协议、域名、端口任一不同 跨域。跨域请求前端页面浏览器中调用了非同源的后端接口。现象后端收到请求前端拿不到数据浏览器报 CORS 错误。解决方案后端配置CORS 跨域中间件通过响应头声明访问白名单。跨域只限制浏览器接口测试工具、客户端 APP 不受影响。