FastAPI中间件:如何处理跨域、认证等请求拦截

FastAPI中间件:处理跨域、认证等请求拦截

在Web开发中,我们经常需要在请求到达后端处理逻辑之前,对请求进行统一检查或处理。比如,判断用户是否有权限访问API、处理前端跨域请求等。FastAPI的中间件(Middleware) 就是用来实现这类功能的“拦截器”——它可以在请求进入应用和响应返回之前,对请求数据进行修改、验证或直接返回响应,让我们能更灵活地控制请求的生命周期。

一、中间件是什么?

简单来说,中间件是一个函数,它会在请求到达路由函数之前(进入应用)和响应返回给客户端之前(离开应用)被调用。你可以把它想象成一条“检查线”:所有请求必须先通过这条线的检查,才能继续处理;响应返回前,也可以通过这条线做最后的调整。

每个中间件都有两个核心作用:
1. 处理请求:在请求到达路由前,对请求数据进行验证、修改或拦截。
2. 处理响应:在响应返回前,对响应数据进行统一处理(比如添加CORS头、修改状态码等)。

中间件的执行顺序非常重要:先注册的中间件会先执行,返回时则相反(后注册的先执行)。

二、处理跨域请求(CORS)

当你的前端页面(比如http://localhost:3000)需要调用后端API(比如http://localhost:8000)时,浏览器会因为“同源策略”阻止跨域请求(不同源:协议、域名、端口任意一个不同)。这时候就需要后端配置跨域资源共享(CORS),允许指定的前端源访问API。

1. 安装与配置CORS中间件

FastAPI通过CORSMiddleware处理CORS,需要先安装fastapiuvicorn(如果还没装):

pip install fastapi uvicorn

然后在代码中导入并配置中间件:

from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

app = FastAPI()

# 配置CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许的前端源,"*"表示所有源(开发环境用,生产环境建议指定具体域名)
    allow_credentials=True,  # 是否允许携带Cookie
    allow_methods=["*"],  # 允许的HTTP方法(GET/POST等),"*"表示所有方法
    allow_headers=["*"],  # 允许的请求头,"*"表示所有头信息
)

# 测试路由
@app.get("/api/hello")
async def hello():
    return {"message": "Hello, Cross-Origin!"}

2. 关键参数说明

  • allow_origins:允许的前端域名列表,生产环境必须写具体域名(如["https://your-frontend.com"]),避免安全风险。
  • allow_methods:允许的HTTP方法(如["GET", "POST"]),不建议用"*"
  • allow_headers:允许的请求头(如["Authorization"]),如果前端需要传递自定义头(如Token),需在此列出。

三、认证拦截:验证用户身份

很多API需要验证用户身份(比如检查Token),中间件可以在所有请求到达路由前统一拦截,验证用户是否有权限访问。如果验证失败,直接返回“未授权”(401)错误,阻止请求进入路由函数。

1. 依赖项 vs 中间件

  • 依赖项:更灵活,针对特定路由或函数,需要显式声明在路由参数中。
  • 中间件:全局生效,无需在每个路由声明,适合通用逻辑(如所有API都需要的认证检查)。

这里用中间件实现全局Token验证

from fastapi import Depends, Request, HTTPException
from fastapi.security import OAuth2PasswordBearer

# 模拟Token验证逻辑(实际项目中需用JWT等真实验证)
def verify_token(token: str) -> bool:
    """验证Token是否有效(示例:简单检查Token是否为"valid_token")"""
    return token == "valid_token"

# OAuth2密码流,获取请求头中的Token(前端通常放在Authorization: Bearer {token})
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 定义认证中间件
class AuthMiddleware:
    async def __call__(self, request: Request, call_next):
        # 1. 获取请求头中的Token
        token = request.headers.get("Authorization")
        if not token or not token.startswith("Bearer "):
            raise HTTPException(status_code=401, detail="Token不存在或格式错误")

        # 2. 提取Token内容(去除"Bearer "前缀)
        token = token.split(" ")[1]

        # 3. 验证Token
        if not verify_token(token):
            raise HTTPException(status_code=401, detail="Token无效或已过期")

        # 4. Token验证通过,继续执行下一个中间件或路由函数
        response = await call_next(request)
        return response

# 将中间件添加到FastAPI应用
app.add_middleware(AuthMiddleware)

# 测试路由(只有携带有效Token才能访问)
@app.get("/api/protected")
async def protected_route():
    return {"message": "这是需要认证的API!"}

2. 如何测试?

启动服务后,用curl或Postman测试:

# 携带有效Token(Token为"valid_token")
curl -H "Authorization: Bearer valid_token" http://localhost:8000/api/protected

# 无效Token(返回401)
curl -H "Authorization: Bearer invalid_token" http://localhost:8000/api/protected

四、中间件的注意事项

  1. 执行顺序:多个中间件按注册顺序执行,返回时逆序。例如:
   app.add_middleware(Middleware1)
   app.add_middleware(Middleware2)

请求会先经过Middleware1,再Middleware2,最后到路由;返回时先经过Middleware2,再Middleware1

  1. 避免过度拦截:中间件逻辑要简洁,避免复杂计算或数据库操作,否则会影响性能。

  2. 区分场景:通用逻辑(如CORS、全局认证)用中间件;局部逻辑(如特定路由的权限)用依赖项。

总结

中间件是FastAPI中处理请求拦截的“瑞士军刀”,能帮我们实现跨域处理、全局认证、日志记录等功能。通过CORSMiddleware解决前端跨域问题,通过自定义中间件实现全局认证,让API开发更高效、安全。

小技巧:开发时先用allow_origins=["*"]快速调试跨域,生产环境务必替换为具体域名;认证中间件要优先处理Token验证,确保逻辑清晰、错误提示明确。

现在,你可以尝试在自己的FastAPI项目中添加中间件,让API更健壮吧!

小夜