跨域资源共享 CROS
是一种浏览器安全机制,用于允许运行在一个源(Origin)的Web应用通过浏览器向另一个源的服务器发起跨域HTTP请求,并在服务器授权的前提下获取资源。
即 允许服务器"告诉"浏览器,哪些来自不同源(域名/端口/协议)的请求可以被放行。
1. 什么是"同源策略"(Same-Origin Policy)?
这是浏览器的默认安全规则。它规定:如果两个 URL 的协议、域名、端口有一个不同,浏览器就不允许它们互相访问数据。
同源示例:http://localhost:8000/api/users 和 http://localhost:8000/docs(同协议、同域名、同端口)。
跨域示例(触犯 CORS):前端页面在 http://localhost:5173,AJAX 请求发往 http://localhost:8000/api/users(端口不同,浏览器会拦截响应)。
2. 为什么要这样设计?
为了防止恶意网站通过脚本窃取你在其他网站(如网银)的登录凭证(Cookie)
真实的报错场景模拟:
前端用 axios 发 POST 请求,数据是 JSON 格式,后端是 FastAPI(没配置 CORS 中间件)。
第一阶段:浏览器(客户端)的"自动安全检查"
前端代码执行:你写了一句 axios.post('http://localhost:8000/users', {name: '张三'})。
浏览器检查请求头:浏览器发现 Content-Type 是 application/json,根据规则,这种请求不能直接发,必须先发一个"探测包"(预检请求),问问服务器同不同意。
探测包发出:浏览器自动生成一个 OPTIONS 请求,发往 http://localhost:8000/users。这个包里的关键信息是:Origin: http://localhost:5173(我是谁)和 Access-Control-Request-Method: POST(我想干什么)。
第二阶段:FastAPI 服务器(没配 CORS)的处理
服务器收到探测包:FastAPI 收到 OPTIONS /users 请求。因为你只写了 @app.post("/users"),没有写 @app.options("/users"),所以 FastAPI 找不到这个路由。
服务器回应探测包:FastAPI 返回一个 404 Not Found 响应。这个响应头里没有 Access-Control-Allow-Origin 字段。
第三阶段:浏览器收到探测响应后的判决
浏览器检查探测响应:浏览器收到了 404 响应,低头一看响应头,发现没有 Access-Control-Allow-Origin。
浏览器直接终止流程:浏览器判定"预检失败",直接把刚才那个 POST /users 请求丢进垃圾桶,压根没往服务器发。
前端报错:浏览器控制台报 CORS error,你的 axios 代码走到了 .catch() 里面。
这个阶段你的业务代码(插入数据库)完全没有执行,因为请求根本没到 FastAPI 的业务层。
第四阶段:如果探测通过了,但正式请求出问题("数据入库但前端报错"的情况)
假设你因为某种原因(比如框架默认支持或者你碰巧配了 OPTIONS)让探测包通过了,浏览器正式发出了 POST 请求。
正式请求到达 FastAPI:请求进入 @app.post("/users"),你的代码执行 db.add(user); await db.commit(); ------ 这时候数据已经成功写入 MySQL 了。
FastAPI 返回正式响应:你的代码最后 return new_user,FastAPI 把状态码 200 和 JSON 数据打包好,发回浏览器。但注意:因为没有全局 CORS 中间件,这个响应头里同样没有 Access-Control-Allow-Origin。
浏览器拦截响应数据:浏览器收到了数据(其实数据已经在网线里跑回来了),但检查响应头时发现没有白名单,它会把这份 JSON 数据拦截在浏览器内核里,坚决不交给前端的 axios 代码。
结果:MySQL 里多了条记录(业务跑了),但前端 axios 的 .then() 永远拿不到返回值,控制台报 CORS 错。
第五阶段:装好 CORSMiddleware 后发生了什么?
中间件拦截探测包:OPTIONS 请求到达后,CORSMiddleware 在路由匹配之前就截获了它,直接返回一个 200 响应,并在响应头里加上 Access-Control-Allow-Origin: http://localhost:5173。
浏览器放行正式请求:浏览器拿到允许头,立刻发出真正的 POST 请求。
中间件二次加工响应:POST 请求跑完业务逻辑后,返回的响应在离开服务器前,再次被 CORSMiddleware 拦截,自动在响应头里补上 Access-Control-Allow-Origin。
完美交付:浏览器看到响应头有白名单,不再拦截,把 JSON 数据交给 axios 的 .then() 回调,前端页面成功拿到数据。
CORS 是浏览器施加的安全锁,FastAPI 本身不拦截任何请求(所有请求都照常执行),它只是没有返回让浏览器放行的响应头。你需要通过 CORSMiddleware 主动告诉浏览器:"这个前端源是我信任的,请放行它的响应数据。"
CORS中间件:
from fastapi.middleware.cors import CORSMiddleware
2.定义允许的来源(可以是域名列表)
origins=[
...等等
]
3.添加 CORS 中间件
fastapi示例名.add_middleware(
CORSMiddleware,
allow_origins="\*", # 允许的源,开发阶段允许所有(*),生产环境需要指定源origins
allow_credentials=True, # 允许携带cookie
allow_methods="\*", # 允许的请求方法
allow_headers="\*", # 允许的请求头
)