本机服务与前端协作
本机服务与前端协作 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
这一课把 Python 服务接到真实前端上:核对地址端口、配 CORS、对齐数据契约,并把「服务没起」「重复提交」这类联调日常变成可预期的处理。
问题场景
前端跑在 http://localhost:3000(Next.js),Python 服务在 http://127.0.0.1:8100。浏览器里 fetch 一发,控制台一排红:CORS 策略拦下、Failed to fetch。修好跨域又发现前端期望 createdAt,后端返回 created_at。这就是本机联调三大件:地址、跨域、契约。
核心解释
跨域:3000 调 8100 属于跨源,浏览器先发 OPTIONS 预检,服务不点头就拦。FastAPI 用中间件声明允许的源:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # 本机前端;生产环境收窄到实际域名
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Content-Type"],
)
注意三点:allow_origins=["*"] 在带凭据(cookie)的请求下无效,本机开发明确写端口最稳;改了中间件要重启 uvicorn;判断「服务不可达」先在浏览器直接访问 http://127.0.0.1:8100/docs,能开就是跨域问题,开不了就是服务没起。
数据契约:前后端对字段名、结构、状态码的共识。本站的做法值得照搬——app/api 各路由统一返回 {total, items} 外壳,错误统一 {error: {category, message}},前端处理逻辑全站一套。Python 侧等价实现就是 Pydantic Out 模型:字段名定死,加字段不破坏老前端。
重复调用:React 19 的 StrictMode 下 useEffect 会执行两次,加上按钮双击,同一笔记可能写两遍。防线两边都设:后端靠唯一约束返回 409(P11 已建),前端靠加载态禁用按钮。后端防线是底线——前端的 loading 有 bug 也炸不出重复数据。
@app.post("/notes", status_code=201)
def create_note(note: NoteIn, conn=Depends(get_db)):
try:
with conn:
cur = conn.execute("INSERT INTO note (id, title) VALUES (?, ?)",
(f"note_{note.title}", note.title))
return {"id": cur.lastrowid, "title": note.title}
except ValueError:
raise HTTPException(status_code=409, detail="笔记已存在,请勿重复提交")
示例拆解
前端侧的最小对接代码,放在 Next.js 页面里验证全链路:
// app/test-api/page.tsx(联调用,验证后删)
async function loadNotes() {
try {
const res = await fetch("http://127.0.0.1:8100/notes");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
console.log(data.total, data.items); // 契约:total + items
} catch {
console.error("服务不可达:先确认 uvicorn 是否在跑");
}
}
预期验证顺序:先关掉 uvicorn 请求一次,看到「服务不可达」的兜底信息;再启动服务请求,拿到 total + items;然后去把 allow_origins 里端口改成 3001 重启,复现 CORS 报错——三种失败各自长什么样,见过一次以后就不用猜了。
动手任务
- 给服务配 CORS 允许
localhost:3000,用一个临时 Next.js 页面成功 fetch/notes。 - 故意改错端口(8101)复现「服务不可达」,再核对
netstat -ano | findstr 8100确认正确端口在监听。 - 给「创建笔记」按钮加双击防护(loading 态 + 禁用),并确认后端唯一约束对第二次请求返回 409。
常见错误
- localhost vs 127.0.0.1 混用:对 CORS 是两个不同的源,允许了 127.0.0.1 拦 localhost;两边写法统一。
- 以为 CORS 是服务端拦截:它是浏览器行为,curl 正常、浏览器报错就是它;解法在响应头不在请求。
- 契约靠口头约定:字段名两边各写一份迟早漂移;用 Pydantic 模型生成/导出契约,P19 的契约测试兜底。
- 前端吞掉非 2xx:fetch 对 404/409 不抛异常,必须手动判
res.ok,否则错误被当成功渲染。
检查与复盘
自查清单:
- 能在 30 秒内定位「跨域、未启动、端口错」三种联调报错
- 响应结构全站统一(total+items / error{category,message}),前端只写一套处理
- 重复写入有两层防线:前端加载态 + 后端唯一约束 409
参考资料
- FastAPI 官方教程:CORS —— 中间件参数逐项说明
- MDN:跨源资源共享(CORS) —— 预检与响应头机制
读完这一课了?
标记完成后,工作台会安排接下来的学习。