FastAPI 如何处理 CORS(跨域资源共享)
现代 Web 开发中,前端与后端交互频繁。然而,浏览器为了保障用户安全,实施了同源策略。所谓同源,要求协议、域名和端口完全相同。若请求的源与当前页面的源不一致,即协议、域名、端口有其一不同,就会触发跨域问题。比如,前端页面在 http://localhost:3000
运行,而后端 API 部署在 http://api.shanhai.com
或者 https://moyu.cn:8000
,此时前端向后端发起资源请求,就会因跨域限制而受阻。
CORS 基础概念
CORS 定义
CORS(Cross - Origin Resource Sharing,跨域资源共享)是一种机制,它允许浏览器向跨源服务器发出 XMLHttpRequest 请求,巧妙地克服了浏览器的同源策略限制。通过 CORS,服务器能够明确告知浏览器,哪些跨源请求是被允许的,从而实现安全的跨域数据传输。
关键 HTTP 头
Origin:在请求头中,Origin
字段用于表明请求来自哪个源。例如,当浏览器向服务器发送请求时,Origin
头可能包含 http://localhost:3000
,服务器可以据此判断请求的来源。
Access - Control - Allow - Origin:这是响应头中的字段,用于指定哪些源可以访问该资源。如果其值设置为 *
,则表示允许所有源访问该资源。不过,在实际生产环境中,出于安全考虑,通常会指定具体的源。
Access - Control - Allow - Methods:该头指定了允许的 HTTP 方法,如 GET
、POST
、PUT
、DELETE
等。服务器通过此头告知浏览器,客户端可以使用哪些方法来请求资源。
Access - Control - Allow - Headers:此头用于指定允许的请求头。当客户端请求中包含自定义头时,服务器需要通过这个头来表明是否允许这些头参与请求。
FastAPI 中处理 CORS
安装依赖
在 FastAPI 中处理 CORS,通常无需额外安装 fastapi - cors
库,因为 fastapi
自身已经包含了处理 CORS 所需的模块。但如果使用的是较旧版本或有特殊需求,可通过 pip install fastapi - cors
安装。
启用 CORS
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 允许所有源访问,实际应按需调整
origins = ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins = origins,
allow_credentials = True,
allow_methods = ["*"],
allow_headers = ["*"]
)
@app.get("/")
def read_root():
return {"message": "欢迎,山海摸鱼人!"}
配置参数
allow_origins:这是一个列表,用于指定允许的源。例如 ["http://localhost:3000", "https://example.com"]
,只有在列表中的源才能访问 FastAPI 应用。
allow_credentials:布尔值,若设置为 True
,表示允许客户端在跨域请求中携带认证信息,如 Cookie
等。
allow_methods:列表形式,指定允许的 HTTP 方法,如 ["GET", "POST"]
,若设置为 *
,则表示允许所有 HTTP 方法。
allow_headers:同样是列表,用于指定允许的请求头。设置为 *
时,允许所有请求头。
完整代码示例
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 允许特定源访问
origins = ["http://localhost:3000"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["Content - Type"]
)
@app.get("/")
def read_root():
return {"message": "欢迎,山海闲游者!"}
@app.post("/data")
asyncdef post_data(request: Request):
data = await request.json()
return {"received_data": data}
运行与测试
将上述代码保存为 main.py
,在终端运行 uvicorn main:app --reload
启动 FastAPI 应用模拟后端。
创建测试的test_main.py 和 templates/index.html
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/")
asyncdef read_root(request: Request):
data = {"name": "山海摸鱼人"}
return templates.TemplateResponse("index.html", {"request": request, **data})
if __name__ == '__main__':
import uvicorn
# 启动 uvicorn 服务器,指定应用实例和监听地址、端口
uvicorn.run(app, host="0.0.0.0", port=3000)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>发送请求到 http://localhost:8000/</title>
</head>
<body>
{{ name }}
<button onclick="fetchData()">发送请求</button>
<div id="response"></div>
<script>
function fetchData() {
fetch('http://localhost:8000/')
.then(response => {
if (!response.ok) {
thrownewError('网络响应错误');
}
return response.json();
})
.then(data => {
const responseDiv = document.getElementById('response');
responseDiv.textContent = JSON.stringify(data);
})
.catch(error => {
const responseDiv = document.getElementById('response');
responseDiv.textContent = `请求出错: ${error.message}`;
});
}
</script>
</body>
</html>
运行test_main.py 模拟前端。
测试1origins = ["http://localhost:3000"]
origins
域名不匹配时,无法跨域请求。localhost
和 127.0.0.1
虽然通常指向同一台机器,但在 CORS 匹配中被视为不同的源。
要注意协议(http 或 https)、域名和端口都必须完全匹配。
测试2将origins
改成origins = ["http://localhost:5000"]
origins
端口不匹配时,无法跨域请求。
测试3将origins
改成origins = []
origins
没有添加内容时,无法跨域请求。
来源:山海摸鱼人