FastAPI CORS 配置
问题描述
在使用 FastAPI 开发 Web 应用时,经常会遇到跨域资源共享 (CORS) 问题。当浏览器从不同域、协议或端口的网页发起请求时,出于安全考虑会阻止这些跨域请求。FastAPI 提供了内置的 CORSMiddleware 来解决这个问题,但有时配置可能不会按预期工作。
基本解决方案
FastAPI 通过 CORSMiddleware
提供 CORS 支持。以下是正确的配置方式:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 允许的源列表
origins = [
"http://localhost:3000", # React 开发服务器
"http://localhost:8080", # Vue 开发服务器
# 添加其他需要允许的源
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 允许的源列表
allow_credentials=True, # 是否允许携带 cookie
allow_methods=["*"], # 允许的 HTTP 方法
allow_headers=["*"], # 允许的请求头
)
@app.get("/")
async def main():
return {"message": "Hello World"}
配置参数详解
allow_origins
指定允许跨域请求的来源列表。对于开发环境,可以使用 ["*"]
允许所有来源,但在生产环境中应该明确指定允许的域名。
allow_credentials
指示是否允许浏览器在跨域请求中发送 cookies 和认证信息。
allow_methods
允许的 HTTP 方法列表,如 ["GET", "POST", "PUT", "DELETE"]
。
allow_headers
允许的请求头列表,可以使用 ["*"]
允许所有头信息。
其他可选参数
expose_headers
: 允许浏览器访问的响应头列表max_age
: 预检请求的缓存时间(秒)
常见问题与解决方案
1. 本地文件访问的特殊情况
安全风险
当客户端从本地文件(如 file://
协议)发起请求时,请求源会是 'null'
。这种情况下需要特殊处理:
app.add_middleware(
CORSMiddleware,
allow_origins=['null'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*']
)
请注意,这种配置有安全风险,不建议在生产环境使用。
2. 服务器端错误导致 CORS 问题
有时服务器端错误(如 Pydantic 验证失败或数据库查询错误)可能导致 CORS 头信息未能正确设置。确保服务器端代码没有隐藏的错误:
# 错误示例:缺少 await 关键字
@app.get("/error")
async def main():
# 这将导致服务器错误,可能影响 CORS 设置
ret = mongo_db.engine.find(Book, limit=10) # 缺少 await
return ret
3. 重复初始化 FastAPI 实例
确保没有在代码的其他地方重复创建 FastAPI 实例,这会导致 CORS 配置被覆盖:
app = FastAPI()
app.add_middleware(CORSMiddleware, ...)
# ... 其他代码 ...
# 错误:再次创建 FastAPI 实例会覆盖之前的配置
app = FastAPI() # CORS 配置丢失
4. 需要暴露自定义响应头
如果前端需要访问自定义响应头,需要使用 expose_headers
参数:
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
expose_headers=["Custom-Header", "Another-Header"]
)
完整示例
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 配置 CORS
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"http://localhost:8080",
"https://your-production-domain.com"
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
expose_headers=["Content-Range", "X-Total-Count"]
)
@app.get("/")
async def read_main():
return {"message": "Hello World!"}
@app.get("/api/items")
async def get_items():
return {"items": ["item1", "item2", "item3"]}
测试 CORS 配置
可以使用 curl 或浏览器开发者工具验证 CORS 配置是否生效:
# 使用 curl 测试 OPTIONS 预检请求
curl -X OPTIONS http://localhost:8000/api/items \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Content-Type" \
-v
安全最佳实践
重要安全提示
- 在生产环境中避免使用
allow_origins=["*"]
- 明确指定允许的域名列表
- 根据需要最小化
allow_methods
和allow_headers
的范围 - 定期审查和更新 CORS 配置
通过正确配置 FastAPI 的 CORS 中间件,您可以安全地处理跨域请求,同时确保应用程序的安全性。