全栈开发中,Python作为后端语言与前端框架的集成是常见需求。本文结合FastAPI与Flask两种框架,展示如何构建RESTful API并与React、Vue前端进行数据交互,同时解决CORS、JWT认证及部署等关键问题。
一、后端API设计:FastAPI与Flask实现CRUD
1.1 FastAPI版本
FastAPI基于Pydantic模型自动生成文档,适合构建高性能API。以下代码演示了商品的增删改查操作:- from fastapi import FastAPI
- from pydantic import BaseModel
- from typing import List
- app = FastAPI()
- class Item(BaseModel):
- id: int
- name: str
- price: float
- is_offer: bool = None
- items = []
- @app.get("/")
- def read_root():
- return {"message": "Hello, World!"}
- @app.get("/items/{item_id}")
- def read_item(item_id: int):
- for item in items:
- if item.id == item_id:
- return item
- return {"error": "Item not found"}
- @app.post("/items/")
- def create_item(item: Item):
- items.append(item)
- return item
- @app.put("/items/{item_id}")
- def update_item(item_id: int, item: Item):
- for i, existing_item in enumerate(items):
- if existing_item.id == item_id:
- items[i] = item
- return item
- return {"error": "Item not found"}
- @app.delete("/items/{item_id}")
- def delete_item(item_id: int):
- for i, item in enumerate(items):
- if item.id == item_id:
- items.pop(i)
- return {"message": "Item deleted"}
- return {"error": "Item not found"}
复制代码
1.2 Flask版本
Flask轻量灵活,使用request和jsonify处理JSON请求与响应:- from flask import Flask, request, jsonify
- app = Flask(__name__)
- items = []
- @app.route('/', methods=['GET'])
- def read_root():
- return jsonify({"message": "Hello, World!"})
- @app.route('/items/<int:item_id>', methods=['GET'])
- def read_item(item_id):
- for item in items:
- if item['id'] == item_id:
- return jsonify(item)
- return jsonify({"error": "Item not found"})
- @app.route('/items/', methods=['POST'])
- def create_item():
- item = request.get_json()
- items.append(item)
- return jsonify(item)
- @app.route('/items/<int:item_id>', methods=['PUT'])
- def update_item(item_id):
- item = request.get_json()
- for i, existing_item in enumerate(items):
- if existing_item['id'] == item_id:
- items[i] = item
- return jsonify(item)
- return jsonify({"error": "Item not found"})
- @app.route('/items/<int:item_id>', methods=['DELETE'])
- def delete_item(item_id):
- for i, item in enumerate(items):
- if item['id'] == item_id:
- items.pop(i)
- return jsonify({"message": "Item deleted"})
- return jsonify({"error": "Item not found"})
- if __name__ == '__main__':
- app.run(debug=True)
复制代码
二、前端集成:React与Vue调用API
2.1 React集成
使用useState和useEffect实现数据获取与提交:- import React, { useState, useEffect } from 'react';
- function App() {
- const [items, setItems] = useState([]);
- const [newItem, setNewItem] = useState({ id: '', name: '', price: '', is_offer: false });
- useEffect(() => {
- fetch('http://localhost:8000/items/')
- .then(response => response.json())
- .then(data => setItems(data));
- }, []);
- const handleSubmit = (e) => {
- e.preventDefault();
- fetch('http://localhost:8000/items/', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(newItem),
- })
- .then(response => response.json())
- .then(data => {
- setItems([...items, data]);
- setNewItem({ id: '', name: '', price: '', is_offer: false });
- });
- };
- return (
- <div>
- <h1>Items</h1>
- <ul>
- {items.map(item => (
- <li key={item.id}>{item.name} - ${item.price}</li>
- ))}
- </ul>
- <form onSubmit={handleSubmit}>
- <input type="text" placeholder="ID" value={newItem.id} onChange={(e) => setNewItem({...newItem, id: parseInt(e.target.value)})} />
- <input type="text" placeholder="Name" value={newItem.name} onChange={(e) => setNewItem({...newItem, name: e.target.value})} />
- <input type="number" placeholder="Price" value={newItem.price} onChange={(e) => setNewItem({...newItem, price: parseFloat(e.target.value)})} />
- <button type="submit">Add Item</button>
- </form>
- </div>
- );
- }
- export default App;
复制代码
2.2 Vue集成
使用Vue的data、mounted和methods实现同样功能:- <template>
- <div>
- <h1>Items</h1>
- <ul>
- <li v-for="item in items" :key="item.id">{{ item.name }} - ${{ item.price }}</li>
- </ul>
- <form @submit.prevent="handleSubmit">
- <input type="text" placeholder="ID" v-model.number="newItem.id" />
- <input type="text" placeholder="Name" v-model="newItem.name" />
- <input type="number" placeholder="Price" v-model.number="newItem.price" />
- <button type="submit">Add Item</button>
- </form>
- </div>
- </template>
- <script>
- export default {
- data() {
- return {
- items: [],
- newItem: { id: '', name: '', price: '', is_offer: false }
- };
- },
- mounted() {
- this.fetchItems();
- },
- methods: {
- fetchItems() {
- fetch('http://localhost:8000/items/')
- .then(response => response.json())
- .then(data => { this.items = data; });
- },
- handleSubmit() {
- fetch('http://localhost:8000/items/', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(this.newItem),
- })
- .then(response => response.json())
- .then(data => {
- this.items.push(data);
- this.newItem = { id: '', name: '', price: '', is_offer: false };
- });
- }
- }
- };
复制代码
三、数据传输与CORS处理
默认JSON格式传输,但前后端域名不同时会触发跨域错误。FastAPI和Flask均需显式配置CORS。
FastAPI中通过CORSMiddleware允许所有来源(生产环境应限定域名):- from fastapi.middleware.cors import CORSMiddleware
- app.add_middleware(
- CORSMiddleware,
- allow_origins=["*"],
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"]
- )
复制代码
Flask中安装flask-cors后直接初始化:- from flask_cors import CORS
- app = Flask(__name__)
- CORS(app)
复制代码
四、JWT认证实现
FastAPI结合python-jose库实现JWT生成与验证。以下模拟用户数据库进行登录与保护路由:- from fastapi import FastAPI, Depends, HTTPException, status
- from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
- from jose import JWTError, jwt
- from datetime import datetime, timedelta
- SECRET_KEY = "your-secret-key"
- ALGORITHM = "HS256"
- ACCESS_TOKEN_EXPIRE_MINUTES = 30
- fake_users_db = {
- "alice": {
- "username": "alice",
- "full_name": "Alice Smith",
- "email": "alice@example.com",
- "hashed_password": "fakehashedsecret",
- "disabled": False
- }
- }
- def fake_hash_password(password: str):
- return "fakehashed" + password
- def verify_password(plain_password, hashed_password):
- return hashed_password == fake_hash_password(plain_password)
- def get_user(db, username: str):
- return db.get(username)
- def create_access_token(data: dict, expires_delta: timedelta = None):
- to_encode = data.copy()
- expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
- to_encode.update({"exp": expire})
- return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
- oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
- async def get_current_user(token: str = Depends(oauth2_scheme)):
- credentials_exception = HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Could not validate credentials",
- headers={"WWW-Authenticate": "Bearer"}
- )
- try:
- payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
- username: str = payload.get("sub")
- if username is None:
- raise credentials_exception
- except JWTError:
- raise credentials_exception
- user = get_user(fake_users_db, username=username)
- if user is None:
- raise credentials_exception
- return user
- @app.post("/token")
- async def login(form_data: OAuth2PasswordRequestForm = Depends()):
- user = get_user(fake_users_db, form_data.username)
- if not user or not verify_password(form_data.password, user["hashed_password"]):
- raise HTTPException(status_code=400, detail="Incorrect username or password")
- access_token = create_access_token(
- data={"sub": user["username"]},
- expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
- )
- return {"access_token": access_token, "token_type": "bearer"}
- @app.get("/users/me")
- async def read_users_me(current_user: dict = Depends(get_current_user)):
- return current_user
复制代码
五、部署方案
后端使用Docker容器化部署,Dockerfile示例如下:- FROM python:3.9-slim
- WORKDIR /app
- COPY requirements.txt .
- RUN pip install --no-cache-dir -r requirements.txt
- COPY . .
- EXPOSE 8000
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
复制代码
前端可部署至Vercel(适合React/Next.js)、Netlify(Vue/React)或GitHub Pages。若需前后端联调,使用Docker Compose编排:- version: '3'
- services:
- backend:
- build: ./backend
- ports:
- - "8000:8000"
- frontend:
- build: ./frontend
- ports:
- - "3000:3000"
- depends_on:
- - backend
复制代码
六、常见问题与解决方案
跨域问题通过CORS中间件解决;认证问题使用JWT标准流程;部署时利用Docker保持环境一致性。性能优化方面建议接口加入缓存层(如Redis),并合理设计数据库查询。
总结:Python(FastAPI/Flask)与前端(React/Vue)的组合能够高效构建全栈应用。掌握API设计、CORS配置、JWT认证和容器化部署是打通前后端的关键步骤。实践时可从简单的CRUD项目入手,逐步集成认证与部署,通过项目驱动巩固知识。 |