FastAPI+Vue前后端分离开发 - 最佳实践 (含鉴权设计)

📅 2026/7/6 3:29:56
FastAPI+Vue前后端分离开发 - 最佳实践 (含鉴权设计)
大家好我是Java1234_小锋老师。前后端分离并不神秘后端专注接口和数据前端专注页面和交互。本文用 FastAPI Vue 3 的组合讲清楚怎么搭、怎么协作以及登录鉴权怎么设计才不容易踩坑。1. 为什么选 FastAPI VueFastAPI写接口快、文档自动生成、类型提示友好适合 Python 后端Vue 3上手平缓、生态成熟做管理后台或业务页面都很顺手。两者都是前后端分离里的常见组合后端只返回 JSON前端用 Axios 调接口部署时可以前后端分开上线互不影响。2. 项目怎么拆推荐目录结构示意project/ ├── backend/ # FastAPI │ ├── app/ │ │ ├── main.py │ │ ├── routers/ │ │ ├── models/ │ │ └── core/ # 配置、安全、数据库 │ └── requirements.txt └── frontend/ # Vue 3 Vite ├── src/ │ ├── api/ │ ├── stores/ │ ├── views/ │ └── router/ └── package.json开发时后端跑在http://127.0.0.1:8000前端 Vite 开发服务器跑在5173通过代理或 CORS 解决跨域。3. FastAPI 后端几个值得用的特性3.1 lifespan 替代 on_event生命周期管理以前常用app.on_event(startup)现在更推荐lifespan启动时连数据库、关闭时释放连接逻辑集中在一处。# backend/app/main.pyfromcontextlibimportasynccontextmanagerfromfastapiimportFastAPIasynccontextmanagerasyncdeflifespan(app:FastAPI):# 启动例如初始化数据库连接池print(服务启动连接数据库...)yield# 关闭释放资源print(服务关闭断开连接)appFastAPI(title示例 API,lifespanlifespan)这样比分散的 startup/shutdown 更好维护也是 FastAPI 目前推荐的方式。3.2 Annotated 依赖注入写法更干净用Annotated把「类型」和「依赖」绑在一起路由函数参数一眼能看懂# backend/app/core/deps.pyfromtypingimportAnnotatedfromfastapiimportDepends,HTTPException,statusfromfastapi.securityimportOAuth2PasswordBearerfromjoseimportjwt,JWTError oauth2_schemeOAuth2PasswordBearer(tokenUrl/api/auth/login)asyncdefget_current_user(token:Annotated[str,Depends(oauth2_scheme)]):try:payloadjwt.decode(token,SECRET_KEY,algorithms[HS256])usernamepayload.get(sub)ifnotusername:raiseHTTPException(status_code401,detail无效令牌)return{username:username}exceptJWTError:raiseHTTPException(status_code401,detail令牌已过期或无效)CurrentUserAnnotated[dict,Depends(get_current_user)]路由里直接写user: CurrentUser不用每次重复Depends(get_current_user)。3.3 Pydantic v2 模型校验请求体、响应体用 Pydantic 模型自动校验和文档生成都省心# backend/app/schemas/user.pyfrompydanticimportBaseModel,FieldclassLoginRequest(BaseModel):username:strField(min_length3,max_length32)password:strField(min_length6)classTokenResponse(BaseModel):access_token:strtoken_type:strbearer前端传错字段或长度不够后端会直接返回 422并带上清晰的错误说明。4. Vue 3 前端几个实用写法4.1 Composition API script setupVue 3 默认推荐script setup逻辑和模板更紧凑!-- frontend/src/views/Login.vue -- script setup import { ref } from vue import { useRouter } from vue-router import { useUserStore } from /stores/user import { login } from /api/auth const router useRouter() const userStore useUserStore() const form ref({ username: , password: }) const loading ref(false) async function handleLogin() { loading.value true try { const data await login(form.value) userStore.setToken(data.access_token) router.push(/) } finally { loading.value false } } /script template form submit.preventhandleLogin input v-modelform.username placeholder用户名 / input v-modelform.password typepassword placeholder密码 / button :disabledloading登录/button /form /template4.2 Pinia 管理登录状态比 Vuex 更简单存 Token 和用户信息// frontend/src/stores/user.jsimport{defineStore}frompiniaimport{ref}fromvueexportconstuseUserStoredefineStore(user,(){consttokenref(localStorage.getItem(token)||)constusernameref()functionsetToken(newToken){token.valuenewToken localStorage.setItem(token,newToken)}functionlogout(){token.valueusername.valuelocalStorage.removeItem(token)}return{token,username,setToken,logout}})4.3 Axios 请求拦截器统一带 Token不用在每个接口里手动加 Header// frontend/src/api/request.jsimportaxiosfromaxiosimport{useUserStore}from/stores/userconstrequestaxios.create({baseURL:/api,timeout:10000})request.interceptors.request.use((config){constuserStoreuseUserStore()if(userStore.token){config.headers.AuthorizationBearer${userStore.token}}returnconfig})request.interceptors.response.use((res)res.data,(err){if(err.response?.status401){constuserStoreuseUserStore()userStore.logout()window.location.href/login}returnPromise.reject(err)})exportdefaultrequest401 时自动清 Token 并跳转登录页体验更统一。5. 鉴权设计从登录到接口保护5.1 整体流程用户登录 → 后端校验账号密码 → 签发 JWT → 前端存 Token ↓ 后续请求 Header 带 Authorization: Bearer token ↓ 后端解析 JWT → 拿到用户身份 → 放行或拒绝常见选择JWT Bearer Token。无状态、前后端分离友好敏感操作仍可配合刷新 Token 或短期过期策略。5.2 后端JWT 登录与校验# backend/app/routers/auth.pyfromdatetimeimportdatetime,timedeltafromfastapiimportAPIRouter,HTTPExceptionfromjoseimportjwtfrompasslib.contextimportCryptContext routerAPIRouter(prefix/api/auth,tags[鉴权])pwd_contextCryptContext(schemes[bcrypt],deprecatedauto)SECRET_KEY请改成环境变量里的密钥ALGORITHMHS256defcreate_access_token(username:str)-str:expiredatetime.utcnow()timedelta(hours2)returnjwt.encode({sub:username,exp:expire},SECRET_KEY,algorithmALGORITHM)router.post(/login)asyncdeflogin(body:LoginRequest):# 示例实际应查数据库ifbody.username!adminornotpwd_context.verify(body.password,哈希后的密码):raiseHTTPException(status_code401,detail用户名或密码错误)return{access_token:create_access_token(body.username),token_type:bearer}router.get(/me)asyncdefget_me(user:CurrentUser):returnuser需要登录的接口加上user: CurrentUser即可未带 Token 或 Token 无效会返回 401。5.3 前端路由守卫未登录访问后台页时重定向到登录// frontend/src/router/index.jsimport{createRouter,createWebHistory}fromvue-routerimport{useUserStore}from/stores/userconstroutercreateRouter({history:createWebHistory(),routes:[{path:/login,component:()import(/views/Login.vue)},{path:/,component:()import(/views/Home.vue),meta:{requiresAuth:true}}]})router.beforeEach((to){constuserStoreuseUserStore()if(to.meta.requiresAuth!userStore.token){return/login}})exportdefaultrouter6. 前后端协作的小约定约定项建议接口前缀统一/api和静态资源分开响应格式{ code, message, data }或直接用 HTTP 状态码 业务字段团队内保持一致即可日期格式接口返回2026-11-02或2026-11-02 17:25:17前端格式化展示跨域开发环境 Vite 代理生产环境 Nginx 反代或后端 CORS密码库存 MD5/ bcrypt 等哈希绝不明文测试账号密码需与线上一致规则表格列宽在前端用min-width 自适应布局避免小屏挤成一团。7. 最后锋哥小结下哈FastAPI Vue 做前后端分离核心就三件事后端lifespan 管生命周期Pydantic 管校验JWT 管身份。前端Pinia 存状态Axios 拦截器带 Token路由守卫挡未登录访问。协作统一接口规范、错误码和日期格式比堆新技术更重要。按上面 demo 搭一版最小可运行项目再按需加权限角色、刷新 Token、接口分页等会比一上来追求「完美架构」轻松很多。