FastAPI 写一个 demo 很快,但项目要像样,不能所有代码都堆在 `main.py`。
工程化的 FastAPI 项目,要考虑分层、依赖注入、配置管理、异常处理、中间件、日志、数据库连接和测试。
【一、什么叫工程化】
工程化不是把项目写复杂,而是让项目在多人协作、需求变化、出错排查、上线部署时仍然可维护。
一个 demo 默认一切顺利:
参数一定正确
数据库一定能连上
外部 API 一定返回成功
用户不会乱传数据
工程化项目正好相反:
参数可能错
数据库可能断
模型 API 可能超时
用户可能没权限
任务可能失败
日志必须能查
【二、推荐项目结构】
小型 FastAPI 项目可以这样拆:
app/
main.py
api/
users.py
tasks.py
schemas/
user.py
services/
user_service.py
repositories/
user_repo.py
core/
config.py
security.py
db/
session.py
大概职责:
- api:接收请求,返回响应。
- schemas:Pydantic 请求和响应模型。
- services:业务逻辑。
- repositories:数据库访问。
- core:配置、安全、工具。
- db:数据库连接。
【三、依赖注入是什么】
FastAPI 的 `Depends` 可以把公共逻辑抽出来。
比如获取当前用户:
from fastapi import Depends, HTTPException
def get_current_user(token: str):
user = parse_token(token)
if not user:
raise HTTPException(status_code=401, detail="未登录")
return user
@app.get("/me")
def me(current_user=Depends(get_current_user)):
return current_user
这样每个需要登录的接口都可以复用 `get_current_user`。
依赖注入常用于:
- 获取数据库连接。
- 获取当前用户。
- 校验权限。
- 注入配置。
- 注入第三方客户端。
【四、Pydantic 模型怎么分】
不要一个模型到处用。
例如用户相关可以拆成:
class UserCreate(BaseModel):
username: str
password: str
class UserLogin(BaseModel):
username: str
password: str
class UserOut(BaseModel):
id: int
username: str
注意 `UserOut` 不应该包含 `password` 或 `password_hash`。
请求模型和响应模型分开,是后端安全和维护的基本习惯。
【五、统一异常处理】
项目里不要到处返回杂乱错误。
可以统一异常结构:
{
"code": "USER_NOT_FOUND",
"message": "用户不存在"
}
FastAPI 可以注册异常处理器:
from fastapi import Request
from fastapi.responses import JSONResponse
class BizError(Exception):
def __init__(self, code: str, message: str):
self.code = code
self.message = message
@app.exception_handler(BizError)
async def biz_error_handler(request: Request, exc: BizError):
return JSONResponse(
status_code=400,
content={"code": exc.code, "message": exc.message},
)
这样业务层可以直接抛 `BizError`。
【六、中间件是什么】
中间件是在请求进入接口前、响应返回客户端前执行的一层逻辑。
常见用途:
- 记录请求日志。
- 统计接口耗时。
- 处理跨域 CORS。
- 统一添加响应头。
- 追踪 request_id。
示例:
import time
from fastapi import Request
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.time()
response = await call_next(request)
cost = time.time() - start
print(request.method, request.url.path, cost)
return response
生产项目里建议用 logging,不要直接 print。
【七、配置管理】
不要把数据库密码、JWT 密钥写死在代码里。
推荐用环境变量:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
jwt_secret: str
class Config:
env_file = ".env"
settings = Settings()
`.env` 不要上传 GitHub,`.env.example` 可以上传示例。
【八、测试怎么写】
FastAPI 可以用 `TestClient` 测接口。
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_health():
resp = client.get("/health")
assert resp.status_code == 200
有测试的项目,比只有截图的项目更有工程化说服力。
【九、常见坑】
- 所有代码堆在一个文件。
- 数据库连接在每个接口里重复创建。
- 配置写死,密钥上传 GitHub。
- 只写 happy path,不处理异常。
- 没有日志,出问题只能猜。
- 请求模型和响应模型混用,泄漏敏感字段。
【十、面试常问】
1. FastAPI 的 Depends 有什么用?
Depends 用于依赖注入,可以复用公共逻辑,例如数据库连接、当前用户、权限校验、配置对象等,减少重复代码。
2. 中间件适合做什么?
适合处理请求级别的通用逻辑,比如日志、耗时统计、CORS、request_id、鉴权前置处理等。不适合把复杂业务逻辑都塞进中间件。
3. FastAPI 项目如何分层?
常见分层是 API 层处理请求响应,Schema 层定义数据结构,Service 层写业务逻辑,Repository 层访问数据库,Core 层管理配置和安全工具。