FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南”

先还原一个真实"案发现场"

小张花了一周写完FastAPI接口,用Postman测试全部通过,美滋滋地去睡了个好觉。第二天起床,信心满满地启动Vue项目,npm run dev,然后......控制台一片红Access-Control-Allow-Origin 错误。

赶紧上网搜,装了个flask-cors?不对,我是FastAPI。手忙脚乱加上CORSMiddleware,跨域是解决了,但POST请求又报422 Unprocessable Entity------前端传的JSON格式后端不认。

你可能会问:不就是配置个代理、写个接口吗?错!前后端分离的"分离"二字,坑全藏在细节里。下面我把最终稳定运行的结构和配置贴出来,你直接复制粘贴就能跑。

📁 我的"强迫症"项目结构(治好了我的精神内耗)

以前我喜欢把所有文件堆在一个文件夹里,后来发现维护起来像在垃圾堆里找钥匙。现在的结构长这样,按功能拆分明细,但又不至于过度拆分(别学我一开始拆了20个文件夹,结果自己都找不到东西)。

复制代码
fastapi_vue_project/
├── backend/                 # FastAPI后端
│   ├── app/
│   │   ├── api/            # 路由层(按模块分)
│   │   │   ├── v1/
│   │   │   │   ├── users.py
│   │   │   │   └── tasks.py
│   │   ├── core/           # 配置、安全、数据库连接
│   │   │   ├── config.py
│   │   │   └── database.py
│   │   ├── models/         # SQLAlchemy模型
│   │   ├── schemas/        # Pydantic模型(请求/响应结构)
│   │   ├── services/       # 业务逻辑层
│   │   └── main.py         # 入口
│   ├── requirements.txt
│   └── .env                # 环境变量(别提交到git!)
├── frontend/               # Vue3前端
│   ├── src/
│   │   ├── api/           # 封装axios请求
│   │   ├── views/         # 页面
│   │   ├── router/        # 路由
│   │   └── utils/         # 工具函数
│   └── .env.development   # 开发环境变量
│   └── .env.production    # 生产环境变量
└── docker-compose.yml      # 可选,线上部署用

👆 这个结构我用了很久,大小项目都稳得很。关键是 core/config.py 和 **frontend/.env.***这对"黄金搭档",解决了90%的环境切换问题。

🔌 通信的3个核心配置(少一个都连不上)

1️⃣ FastAPI的CORS中间件(不是加一行就完事的)

很多人复制官方示例allow_origins=["*"]就跑了,但生产环境千万别这么干 !而且你还要注意allow_credentialsallow_headers的配合。

复制代码
# backend/app/core/config.py
class Settings:
    BACKEND_CORS_ORIGINS = ["http://localhost:5173", "http://127.0.0.1:5173"]  # 开发环境
    # 生产环境从环境变量读取,不要写死

# backend/app/main.py
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.BACKEND_CORS_ORIGINS,  # 注意!不是 "*"
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["Authorization", "Content-Type"],
)

这里有个坑:如果你前端用axios 带上了withCredentials: true,后端allow_origins就不能是["*"],必须指定具体域名。当初我在这里卡了4个小时,最后翻FastAPI源码才找到原因。

2️⃣ Vue的代理配置(开发神器,但别滥用)

Vite(或webpack)的proxy 配置简直是开发阶段的救星,让你彻底告别跨域烦恼。但很多人抄完配置就不管了,结果部署到生产环境又报错。

复制代码
// frontend/vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',  // FastAPI默认端口
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')  // 注意这个重写规则!
      }
    }
  }
})        

注意看rewrite这一行:如果你的FastAPI路由是@app.get("/users"),前端请求/api/users,代理会把/api去掉再转发。这里写错了路径,就会404。

3️⃣ 统一的响应格式(别让前端猜你返回什么)

这是我最想吐槽的一点:很多人后端返回的数据结构每天不一样,今天{"data": {...}},明天{"result": {...}}。前端大哥没被你气死算我输。

我的习惯:统一用下面的格式

复制代码
# backend/app/schemas/common.py
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional

T = TypeVar('T')

class ResponseModel(BaseModel, Generic[T]):
    code: int = 200
    message: str = "success"
    data: Optional[T] = None

# 使用示例
@router.get("/users/{user_id}")
def get_user(user_id: int):
    user = service.get_user(user_id)
    return ResponseModel(data=user)

前端axios拦截器统一处理这个结构,代码量直接砍半 。是不是以为这样就完了?不,还有一个关于错误码的约定,建议至少约定401去登录、403无权限、422参数错误,别让前端去猜。

🌍 开发 vs 生产:别再手改baseURL了!

我见过最野的操作:每次部署前,手动把 axios 的 baseURL 从localhost:8000改成线上域名,然后commit,然后......忘了改回来。😱

正确姿势:用环境变量

复制代码
# frontend/.env.development
VITE_API_BASE_URL = '/api'   # 开发走代理

# frontend/.env.production
VITE_API_BASE_URL = 'https://your-api-domain.com'

// frontend/src/api/request.js
const request = axios.create({
    baseURL: import.meta.env.VITE_API_BASE_URL,
    timeout: 10000
})

后端也一样,用python-dotenv加载 .env 文件,永远不要把密钥写死在代码里

💣 再说三个容易翻车的点(都是真金白银换的教训)

⚠️ 第一个:路径拼接的斜杠

后端路由@app.get("/users"),前端请求/users/(多了一个斜杠),在nginx下可能301重定向,cookie丢了。

建议统一规则:路由末尾不加斜杠,前端请求也不加

⚠️ 第二个:请求/响应拦截器里的"循环引用"

有人在拦截器里用response.data.data取数据,但刷新token的接口又走了同一个拦截器,结果死循环。

解决方案:在白名单接口的meta里加一个标记跳过拦截器

⚠️ 第三个:FastAPI的异步陷阱

如果你用了async def,里面却调用同步的SQLAlchemy操作,会阻塞事件循环。

要么全用def,要么用asyncio.to_thread。很多人不知道,上了生产才发现接口越跑越慢。

🧠 进阶思考:拆分vs聚合,你的项目适合哪种?

相关推荐
陈随易22 分钟前
有生之年系列,Nodejs进程管理pm2 v7.0发布
前端·后端·程序员
冰暮流星40 分钟前
javascript之事件代理/事件委托
前端
@yanyu6662 小时前
登录注册功能-明文
vue.js·springboot
陈随易2 小时前
AI时代,你还在坚持手搓文章吗
前端·后端·程序员
里欧跑得慢4 小时前
17. Flutter Hero动画实现:让界面过渡更加优雅
前端·css·flutter·web
IT_陈寒5 小时前
Vue的这个响应式陷阱,我debug了一整天才爬出来
前端·人工智能·后端
kyriewen5 小时前
前端测试:别为了100%覆盖率而写测试,那是自欺欺人
前端·javascript·单元测试
去伪存真5 小时前
我自己写的第一个skills--project-core-standards
前端·agent
Data_Journal5 小时前
如何使用cURL更改User Agent
大数据·服务器·前端·javascript·数据库
竹林8185 小时前
wagmi v2 多链钱包切换:一个 Uniswap 仿盘项目让我踩了三天坑
前端·javascript