FastAPI+Vue3项目实战

🏷️ L3 📊 intermediate ⏱️ 50分钟 🏷️ FastAPI,Vue3,全栈实战,前后端分离,SQLAlchemy,Pinia,前沿

好的,各位学员,大家好!我是你们的IT培训导师。很高兴能和大家一起进入这个充满挑战与乐趣的全栈实战课程。

今天,我们要探讨的主题是“FastAPI + Vue3 项目实战”。这将是一堂干货满满、节奏紧凑的课程,旨在帮助大家快速掌握这套现代、高效的Web开发技术栈。

---

# FastAPI+Vue3项目实战

一、概述

为什么要学习这个主题?

在当今的Web开发领域,前后端分离已成为绝对的主流。选择一个高效、现代、且对开发者友好的技术栈至关重要。

学完本课程能做什么?

完成本课程后,你将具备独立搭建一个功能完备的全栈Web应用的核心能力。具体来说,你可以:

1. 独立设计并实现RESTful API:掌握从路由设计、请求校验到响应封装的完整流程。 2. 熟练使用SQLAlchemy进行数据库操作:实现模型定义、CRUD操作、复杂查询和事务管理。 3. 搭建基于Vue3 + Pinia的现代化前端:运用组合式API(Composition API)组织逻辑,使用Pinia进行高效的状态管理。 4. 实现安全的JWT用户认证系统:从前端登录到后端Token签发、验证,构建完整的权限闭环。 5. 完成前后端联调与项目部署:解决跨域问题,并将应用部署到生产环境。

适合人群和前置知识要求

---

二、核心知识点

本课程将围绕4个核心模块展开。

模块1:FastAPI后端基石与RESTful API设计

原理讲解:FastAPI基于Starlette构建,利用Python的类型提示(Type Hints)实现自动请求校验自动生成API文档(Swagger UI / ReDoc)。其核心是路径操作装饰器(如@app.get())和依赖注入系统

关键技术对比

| 特性 | FastAPI | Flask | Django REST Framework | | :--- | :--- | :--- | :--- | | 异步支持 | 原生异步(基于ASGI) | 同步为主,异步支持较弱 | 同步为主,异步支持较弱 | | 性能 | 极高(媲美Node.js/Go) | 中等 | 较低 | | 自动文档 | 内置(Swagger/ReDoc) | 需第三方扩展(如Flask-RESTx) | 内置(Browsable API) | | 数据校验 | 基于Pydantic,自动校验 | 需手动或使用Marshmallow | 使用Serializer,功能强大但复杂 |

代码示例


# main.py
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel, Field
from typing import Optional

app = FastAPI(title="我的API", version="1.0.0")

# 1. 数据模型(Pydantic Model) class Item(BaseModel): name: str = Field(..., min_length=1, max_length=50, description="项目名称") price: float = Field(..., gt=0, description="价格") is_offer: Optional[bool] = None

class ItemResponse(BaseModel): id: int name: str price: float is_offer: Optional[bool] = None

# 2. 模拟数据库 fake_db = {}

# 3. 路径操作与参数校验 @app.post("/items/", response_model=ItemResponse, status_code=201) async def create_item(item: Item): """创建一个新项目""" item_id = len(fake_db) + 1 fake_db[item_id] = item.model_dump() return {"id": item_id, **item.model_dump()}

@app.get("/items/{item_id}", response_model=ItemResponse) async def read_item(item_id: int): """根据ID获取项目""" if item_id not in fake_db: raise HTTPException(status_code=404, detail="Item not found") return {"id": item_id, **fake_db[item_id]}


模块2:SQLAlchemy ORM与数据库交互

原理讲解:SQLAlchemy是Python中最强大的ORM(对象关系映射)库。它将数据库表映射为Python类,将数据库操作(SQL语句)转化为面向对象的方法调用,极大地提高了开发效率和代码可读性。我们使用其2.0风格Declarative MappingSession

代码示例


# database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, sessionmaker

# 数据库连接URL(以SQLite为例,生产环境请用PostgreSQL) SQLALCHEMY_DATABASE_URL = "sqlite:///./myapi.db"

engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

class Base(DeclarativeBase): pass

# models.py from sqlalchemy import Column, Integer, String, Float, Boolean from database import Base

class ItemDB(Base): __tablename__ = "items" id = Column(Integer, primary_key=True, index=True) name = Column(String, index=True) price = Column(Float) is_offer = Column(Boolean, default=False)

# crud.py from sqlalchemy.orm import Session from models import ItemDB import schemas # 存放Pydantic模型

def get_item(db: Session, item_id: int): return db.query(ItemDB).filter(ItemDB.id == item_id).first()

def create_item(db: Session, item: schemas.ItemCreate): db_item = ItemDB(**item.model_dump()) db.add(db_item) db.commit() db.refresh(db_item) return db_item


模块3:Vue3组合式API与Pinia状态管理

原理讲解

  • **组合式API (Composition API)**:Vue3引入的`setup`语法糖,允许我们按逻辑功能组织代码,而不是按选项(`data`, `methods`, `computed`)。通过`ref`、`reactive`、`computed`、`watch`等函数,实现更灵活、可复用的逻辑组合。
  • **Pinia**:Vue的官方状态管理库,替代Vuex。它完全支持TypeScript,API简洁直观,采用`Store`的概念,每个Store是一个独立的、响应式的数据仓库。

代码示例


<!-- src/stores/counter.js (Pinia Store) -->
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => { // State const count = ref(0) // Getters const doubleCount = computed(() => count.value * 2) // Actions function increment() { count.value++ }

return { count, doubleCount, increment } })

<!-- src/components/Counter.vue (Vue3组件) --> <script setup> import { useCounterStore } from '@/stores/counter' const counterStore = useCounterStore() </script>

<template> <div> <p>Count: {{ counterStore.count }}</p> <p>Double: {{ counterStore.doubleCount }}</p> <button @click="counterStore.increment">+1</button> </div> </template>


模块4:JWT认证与前后端联调

原理讲解:JWT(JSON Web Token)是一种无状态的认证机制。用户登录成功后,服务器签发一个包含用户身份信息的加密Token,客户端(前端)在后续请求的Authorization头中携带此Token。服务器通过验证Token来确认用户身份,无需在服务端存储会话信息。

前后端联调关键跨域资源共享 (CORS)。前端(如localhost:5173)访问后端(如localhost:8000)时,浏览器会默认阻止跨域请求。我们需要在后端配置允许跨域的来源。

代码示例


# main.py (后端配置CORS和JWT)
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext

# ... (省略其他import)

# CORS配置 app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173"], # 允许前端地址 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )

# JWT配置 SECRET_KEY = "your-secret-key-here" # 生产环境务必使用强随机密钥 ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 创建Token def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt

# 登录接口 @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): # ... 验证用户名密码 ... access_token = create_access_token(data={"sub": user.username}) return {"access_token": access_token, "token_type": "bearer"}

# 受保护的路由 @app.get("/users/me") async def read_users_me(token: str = Depends(oauth2_scheme)): # ... 解码Token,获取用户信息 ... return current_user


---

三、实操步骤

我们将从零开始,构建一个简单的“待办事项 (Todo)”应用。

步骤1:搭建FastAPI后端骨架

1. 创建项目目录并安装依赖


mkdir todo-fullstack
cd todo-fullstack
mkdir backend
cd backend
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install fastapi uvicorn sqlalchemy python-jose[cryptography] passlib[bcrypt] python-multipart

2. 创建main.py,写入模块1和模块4的代码框架,并添加一个简单的健康检查路由/


# main.py
from fastapi import FastAPI
app = FastAPI()

@app.get("/") async def root(): return {"message": "Hello World"}


3. 启动服务


uvicorn main:app --reload

预期效果:访问 http://127.0.0.1:8000/docs 可以看到Swagger API文档。

步骤2:集成SQLAlchemy

1. 创建database.pymodels.pyschemas.pycrud.py文件,内容参考模块2。 2. 在main.py中引入数据库初始化逻辑和依赖注入函数get_db。 3. 重启服务,确保数据库表自动创建。

步骤3:创建Vue3前端项目

1. 在todo-fullstack目录下,打开新终端:


npm create vue@latest frontend
# 选择:添加Pinia, 添加Router, 使用Composition API等
cd frontend
npm install

2.  安装axiosnpm install axios

步骤4:编写前端核心页面 (Todo列表)

1. 创建Pinia Storesrc/stores/todo.js,定义todos状态,以及fetchTodos, addTodo, toggleTodo等actions。 2. 修改src/views/TodoView.vue:使用<script setup>语法,引入todoStore,在onMounted中调用fetchTodos。使用v-for渲染列表,v-model绑定输入框。 3. 配置API请求:在src/utils/request.js中创建axios实例,设置baseURLhttp://127.0.0.1:8000

步骤5:实现JWT登录

1. 后端:实现/token登录接口和/users/me接口(参考模块4)。 2. 前端

  • 创建`LoginView.vue`,包含用户名和密码表单。
  • 登录成功后,将`access_token`存储在`localStorage`中。
  • 在axios请求拦截器中,从`localStorage`读取Token并添加到`Authorization: Bearer xxx`头中。
  • 使用路由守卫(`router.beforeEach`)判断用户是否登录,未登录则跳转到登录页。

步骤6:前后端联调与功能测试

1. 启动后端(端口8000)和前端(端口5173)。 2. 在前端登录,创建、查看、删除Todo项。 3. 打开浏览器开发者工具,查看网络请求,确认Token在请求头中,数据交互正常。

---

四、常见问题与故障排查

问题1:前端请求后端时遇到CORS错误。

  • **现象**:浏览器控制台报错 `Access to XMLHttpRequest at 'http://localhost:8000/...' from origin 'http://localhost:5173' has been blocked by CORS policy`。
  • **判断思路**:后端未正确配置CORS中间件。
  • **解决方法**:
1. 检查后端main.py中是否添加了CORSMiddleware。 2. 确认allow_origins列表中包含了前端的确切地址(如["http://localhost:5173"]),不要忘记协议和端口。 3. 如果使用*允许所有来源,在生产环境中不安全,开发时可以用。

问题2:JWT Token验证失败,接口返回401。

  • **现象**:登录成功后,访问需要认证的接口时返回401 Unauthorized。
  • **判断思路**:
1. Token是否已过期? 2. Token是否被正确发送? 3. 后端解码Token的密钥或算法是否与签发时一致?
  • **排查流程**:
1. 在浏览器开发者工具 -> 网络 -> 请求头中,查看Authorization头是否存在,格式是否为 Bearer <token>。 2. 检查后端SECRET_KEYALGORITHM是否与签发时一致。 3. 检查Token的exp字段,确认未过期。可以通过在[jwt.io](https://jwt.io/)上解码Token查看。

问题3:SQLAlchemy数据库操作报错“Table already exists”或“No such table”。

  • **现象**:启动后端或执行数据库操作时抛出异常。
  • **判断思路**:数据库迁移或初始化问题。
  • **解决方法**:
1. 初次启动:确保在main.pyapp启动事件中调用了Base.metadata.create_all(bind=engine)。 2. 修改模型后:对于开发环境,最简单的做法是删除旧的数据库文件(如myapi.db),让程序重新创建。生产环境应使用Alembic等迁移工具。

问题4:Vue3组件中refreactive数据不更新视图。

  • **现象**:数据在控制台打印已改变,但页面显示没有更新。
  • **判断思路**:响应式丢失。
  • **解决方法**:
1. ref:在<script setup>setup()函数中,通过ref()创建的变量,在模板中会自动解包,但在JavaScript中操作时,必须使用.value属性(如count.value++)。 2. reactive:确保直接修改对象的属性,而不是替换整个对象。例如,正确:state.items.push(newItem);错误:state = { items: [...] }。如果需要替换整个对象

在博海学习网开始学习 →