FastAPI 基础教程
下面给你一份 真正实用、可直接上手项目的 FastAPI 教程。
不是那种只讲概念的,而是结合你现在的技术栈(FastAPI + SQLAlchemy ORM + PostgreSQL + JWT + Casbin + UMI + AntD)去写的 项目级教程。
内容分为 基础 -> 实战 -> 工程化最佳实践 三个部分。
pip install fastapi uvicorn[standard]
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
return {"msg": "Hello FastAPI"}
运行:
uvicorn main:app --reload
访问:http://127.0.0.1:8000/docs
Swagger 文档自动生成。
推荐 模块化结构(你已经在使用):
app/
api/
users.py
auth.py
roles.py
permissions.py
core/
config.py
security.py
db/
base.py
session.py
models/
user.py
role.py
permission.py
schemas/
user.py
role.py
main.py
在 main.py 注册路由:
from fastapi import FastAPI
from api import users, auth
app = FastAPI()
app.include_router(auth.router, prefix="/api/auth")
app.include_router(users.router, prefix="/api/users")
请求模型 + 响应模型:
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
class UserOut(BaseModel):
id: int
username: str
email: str
class Config:
from_attributes = True
FastAPI 会自动验证输入。
你项目中使用 Async SQLAlchemy 2.0 是最佳方案。
# db/session.py
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost:5432/dbname",
echo=False,
)
AsyncSessionLocal = async_sessionmaker(
engine, expire_on_commit=False
)
async def get_db():
async with AsyncSessionLocal() as session:
yield session
# db/models/user.py
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String, Integer
from db.session import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
username: Mapped[str] = mapped_column(String(50), unique=True)
email: Mapped[str] = mapped_column(String(100), unique=True)
password: Mapped[str] = mapped_column(String(255))
pip install python-jose passlib[bcrypt] python-multipart
# core/security.py
from datetime import datetime, timedelta
from jose import jwt
from passlib.context import CryptContext
SECRET_KEY = "your-secret"
ALGORITHM = "HS256"
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str):
return pwd_context.hash(password)
def verify_password(password: str, hashed: str):
return pwd_context.verify(password, hashed)
def create_access_token(data: dict, expires_minutes=60):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
# api/auth.py
from fastapi import APIRouter, Depends, HTTPException
from core.security import verify_password, create_access_token
from db.session import get_db
from sqlalchemy import select
from db.models.user import User
router = APIRouter()
@router.post("/login")
async def login(data: dict, db=Depends(get_db)):
stmt = select(User).where(User.username == data["username"])
result = await db.execute(stmt)
user = result.scalar_one_or_none()
if not user or not verify_password(data["password"], user.password):
raise HTTPException(401, "用户名或密码错误")
token = create_access_token({"id": user.id})
return {"token": token}
安装:
pip install casbin casbin_sqlalchemy_adapter
集成示例(核心代码)
from fastapi import Depends, HTTPException
from casbin import Enforcer
from core.casbin import enforcer
def check_permission(sub: str, obj: str, act: str):
if not enforcer.enforce(sub, obj, act):
raise HTTPException(status_code=403, detail="权限不足")
可在路由中使用:
@router.get("/roles")
async def get_roles(user=Depends(get_current_user)):
check_permission(user.role_code, "roles", "read")
示例:
from datetime import date, datetime
def to_dict_list(result, fields: list[str] | None = None):
data = []
for item in result:
row = {}
for key in fields or item.__dict__.keys():
val = getattr(item, key, None)
if isinstance(val, (datetime, date)):
val = val.strftime("%Y-%m-%d %H:%M:%S") if isinstance(val, datetime) else val.strftime("%Y-%m-%d")
elif isinstance(val, str):
val = val.strip()
row[key] = val
data.append(row)
return data
上传:
from fastapi import UploadFile
@router.post("/upload")
async def upload(file: UploadFile):
content = await file.read()
with open(f"files/{file.filename}", "wb") as f:
f.write(content)
return {"msg": "ok"}
下载:
from fastapi.responses import FileResponse
@router.get("/download")
async def download():
return FileResponse("files/test.pdf")
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
你构建后端系统时,需要以下规范:
使用 pydantic-settings。
你正在封装 async insert/update/delete,可采用:
async def db_insert(db: AsyncSession, obj):
db.add(obj)
await db.commit()
await db.refresh(obj)
return obj
统一输出:
{"code": 400, "msg": "xxx", "data": null}
loguru + 旋转日志。