本文目录导读:

解决Python(尤其是Flask/Django/FastAPI等Web框架)的跨域报错(CORS,Cross-Origin Resource Sharing,跨域资源共享问题),核心是在服务端进行配置,明确告诉浏览器“允许来自其他域名的请求”。
常见的跨域报错信息类似:
Access to XMLHttpRequest at 'http://localhost:5000/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
下面针对不同框架给出具体的解决方法和代码案例。
Flask 解决方案
推荐使用第三方库 flask-cors,它最简洁、最安全。
安装:
pip install flask-cors
全局启用(推荐开发环境或用) 让整个应用的所有路由都支持跨域。
from flask import Flask, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # 默认允许所有来源
@app.route('/api/data')
def get_data():
return jsonify({"message": "Hello from Flask!"})
if __name__ == '__main__':
app.run(debug=True)
指定允许的来源(生产环境推荐) 只允许特定的域名访问,提高安全性。
from flask import Flask, jsonify
from flask_cors import CORS
app = Flask(__name__)
# 只允许 http://localhost:3000 访问
CORS(app, origins='http://localhost:3000')
@app.route('/api/data')
def get_data():
return jsonify({"message": "Hello from specific origin!"})
if __name__ == '__main__':
app.run(debug=True)
仅对单个路由启用(精细控制)
from flask import Flask, jsonify
from flask_cors import cross_origin
app = Flask(__name__)
@app.route('/api/data')
@cross_origin(origins='http://localhost:3000') # 只允许这个路由跨域
def get_data():
return jsonify({"message": "Hello from specific route!"})
if __name__ == '__main__':
app.run(debug=True)
Django 解决方案
推荐使用 django-cors-headers。
安装:
pip install django-cors-headers
配置步骤:
- 在
settings.py的INSTALLED_APPS中添加'corsheaders'。 - 在
MIDDLEWARE中(尽量靠前,通常在CommonMiddleware之前)添加'corsheaders.middleware.CorsMiddleware'。 - 设置允许的来源。
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'...',
'corsheaders', # 添加这一行
'your_app',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 建议放在最前面
'django.middleware.common.CommonMiddleware',
# ... 其他中间件
]
# 方案A: 允许所有来源(开发用,生产环境危险)
CORS_ALLOW_ALL_ORIGINS = True
# 方案B: 指定白名单(生产环境推荐)
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"https://your-frontend.com",
]
# 允许携带 Cookie(如果需要)
CORS_ALLOW_CREDENTIALS = True
# 允许的 HTTP 方法
CORS_ALLOW_METHODS = [
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS',
]
# 允许的请求头
CORS_ALLOW_HEADERS = [
'accept',
'authorization',
'content-type',
'x-csrftoken',
]
FastAPI 解决方案
FastAPI 基于 Starlette,可以使用 starlette.middleware.cors 中间件。
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 配置允许的来源
origins = [
"http://localhost:3000", # 你的前端地址
"http://localhost:5173", # Vite 默认端口
"https://your-frontend.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"], # 允许所有方法
allow_headers=["*"], # 允许所有请求头
)
@app.get("/api/data")
async def get_data():
return {"message": "Hello from FastAPI!"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
通用手动方案(不依赖第三方库)
如果你不想安装第三方库,可以自己手动在响应头中添加 CORS 头。这种方式只适合非常简单的场景,因为还需要处理预检请求(OPTIONS 方法)。
from flask import Flask, jsonify, make_response
app = Flask(__name__)
@app.after_request
def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
return response
@app.route('/api/data')
def get_data():
return jsonify({"message": "Manual CORS"})
if __name__ == '__main__':
app.run(debug=True)
常见问题与排查
-
为什么会报错?
- 浏览器的同源策略,浏览器会阻止从一个域名(如
http://localhost:3000)向另一个域名(如http://localhost:5000)发起的 AJAX 请求,除非服务器明确告诉浏览器“允许这个请求”。 - 同源:协议、域名、端口三者完全相同,只要有一个不同,就构成跨域。
- 浏览器的同源策略,浏览器会阻止从一个域名(如
-
预检请求(Preflight)
- 当请求不是简单请求(例如使用了
PUT/DELETE方法、自定义请求头、Content-Type: application/json)时,浏览器会先发送一个OPTIONS请求(预检请求),询问服务器是否允许后续的正式请求。 - 解决方案:服务器必须正确处理
OPTIONS请求,并返回Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers等响应头,前面的所有代码示例都自动处理了这一点。
- 当请求不是简单请求(例如使用了
-
使用 Nginx 或反向代理
- 如果你的后端(Python 应用)位于 Nginx 之后,你也可以在 Nginx 层面配置 CORS,而不是在 Python 应用层面。
- Nginx 配置示例:
location /api/ { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # ... 其他配置 }
-
生产环境注意事项
- *不要使用 `Access-Control-Allow-Origin: *` 允许所有来源,生产环境中应替换为具体的域名。
- Credentials:如果你的请求需要携带 Cookie 或 Authorization 头,
Access-Control-Allow-Origin*不能是 `**,必须是指定的具体域名,且需要设置Access-Control-Allow-Credentials: true`。
-
兜底方案(不推荐)
- 如果你的后端和前端都部署在同一台服务器上,可以不解决 CORS 问题,而是通过反向代理(如 Nginx 或 Vite 的 proxy 配置)将前端对后端的请求代理到同一个端口下,从而避免跨域。
| 框架 | 推荐方案 | 关键配置 |
|---|---|---|
| Flask | flask-cors |
CORS(app) 或 CORS(app, origins=[...]) |
| Django | django-cors-headers |
在 settings.py 中添加中间件和 CORS_ALLOWED_ORIGINS |
| FastAPI | CORSMiddleware |
app.add_middleware(CORSMiddleware, ...) |
最简单的起步命令:根据你的后端框架,复制上面的“全局启用”代码,将 origins 改为你的前端地址,然后重启后端服务即可。