一、为什么Flask会遇到CORS问题
现代Web开发中,前后端分离已经成为主流架构模式。前端可能运行在http://localhost:3000,而后端API服务运行在http://localhost:5000,浏览器出于安全考虑,会阻止这种跨域请求。这就是所谓的同源策略限制。
Flask作为轻量级Python Web框架,默认没有内置CORS支持。当你的前端应用尝试访问Flask API时,浏览器会抛出类似这样的错误:
Access to XMLHttpRequest at 'http://localhost:5000/api' from origin 'http://localhost:3000' has been blocked by CORS policy
二、解决Flask CORS问题的三种主流方案
2.1 使用Flask-CORS扩展(推荐方案)
这是最简单直接的解决方案。Flask-CORS是专门为Flask设计的扩展,只需几行代码就能搞定。
from flask import Flask
from flask_cors import CORS # 导入CORS模块
app = Flask(__name__)
CORS(app) # 启用CORS,允许所有域名访问所有路由
@app.route("/api")
def hello():
return {"message": "Hello, CORS!"}
if __name__ == "__main__":
app.run()
如果想进行更精细的控制,可以这样配置:
CORS(app, resources={
r"/api/*": {
"origins": ["http://localhost:3000", "https://your-production-site.com"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["Content-Type", "Authorization"]
}
})
2.2 手动添加CORS头信息
如果你不想使用第三方扩展,也可以手动添加响应头:
from flask import Flask, jsonify, make_response
app = Flask(__name__)
@app.route("/api")
def hello():
response = make_response(jsonify({"message": "Hello, CORS!"}))
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE"
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
return response
@app.after_request
def after_request(response):
# 这个钩子函数会在每个请求后执行
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
response.headers.add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS")
return response
if __name__ == "__main__":
app.run()
2.3 使用Nginx反向代理
对于生产环境,更推荐使用Nginx作为反向代理来解决CORS问题:
server {
listen 80;
server_name api.yourdomain.com;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://localhost:5000;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
}
}
三、处理预检请求(OPTIONS)的特殊情况
对于复杂请求(如带自定义头或非简单方法的请求),浏览器会先发送OPTIONS预检请求。我们需要特别处理:
@app.route("/api", methods=["GET", "POST", "OPTIONS"])
def handle_api():
if request.method == "OPTIONS":
# 处理预检请求
response = make_response()
response.headers["Access-Control-Allow-Origin"] = "http://localhost:3000"
response.headers["Access-Control-Allow-Methods"] = "GET, POST"
response.headers["Access-Control-Allow-Headers"] = "Content-Type, X-Custom-Header"
response.headers["Access-Control-Max-Age"] = "86400" # 24小时缓存
return response
# 处理实际请求
return jsonify({"data": "Your actual response here"})
四、实际开发中的注意事项
不要在生产环境使用通配符(*):虽然
Access-Control-Allow-Origin: *很方便,但会带来安全隐患。应该明确指定允许的域名。注意凭据(cookie)的处理:如果需要发送cookie,必须满足三个条件:
CORS(app, supports_credentials=True) # Flask-CORS配置前端请求需要设置:
fetch(url, { credentials: 'include' });并且不能使用通配符,必须指定具体域名:
response.headers["Access-Control-Allow-Origin"] = "http://your-frontend.com"缓存预检请求:通过设置
Access-Control-Max-Age头可以减少OPTIONS请求的次数。处理复杂请求头:如果你的API需要接收自定义头,必须在
Access-Control-Allow-Headers中明确列出。考虑使用环境变量:不同环境(开发/测试/生产)可能需要不同的CORS配置:
allowed_origins = ["http://localhost:3000"] if os.environ.get("FLASK_ENV") == "development" else ["https://production-site.com"] CORS(app, origins=allowed_origins)
五、不同场景下的最佳实践
开发环境:为了方便,可以使用通配符或Flask-CORS的默认配置。
测试环境:应该模拟生产环境的配置,指定具体的测试域名。
生产环境:
- 使用Nginx处理CORS
- 严格限制允许的域名
- 启用HTTPS
- 考虑使用CSRF令牌等额外安全措施
微服务架构:如果有多服务需要共享CORS配置,可以考虑将配置集中管理,或使用API网关统一处理。
六、常见问题排查指南
检查浏览器控制台错误:CORS错误通常会显示详细的错误信息,包括被拒绝的请求方法和缺少的头部。
使用CURL测试:
curl -H "Origin: http://your-frontend.com" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: content-type" -X OPTIONS --verbose http://your-api.com/endpoint检查响应头:确保所有必要的CORS头都存在且值正确。
顺序问题:确保在返回响应前添加了CORS头,特别是在使用错误处理时。
检查Flask路由:确保OPTIONS方法已添加到路由的methods参数中。
七、总结
跨域问题是现代Web开发中无法回避的挑战,但通过合理使用Flask-CORS扩展、手动设置响应头或配置反向代理,我们可以优雅地解决这个问题。关键是根据不同环境选择合适的方案,并始终牢记安全最佳实践。记住,CORS虽然是个小配置,但它关系到整个应用的安全性和可用性,值得投入时间把它做好。
评论