200字
FastAPI学习总结(一)
2026-02-24
2026-02-26

博主之前硕士期间实现课题有简单用到fastapi,当时只是因为项目用到python,而又没学过Django和Flask,考虑此前学习spring的时候对restful比较属性选择了fastapi,姑且算是有一定经验了

tips:warn 创作声明:本文含AI辅助润色,图片生成

引言

FastAPI 是一个基于 Python 类型提示的现代高性能 Web 框架,能快速构建 API 并自动生成交互式文档。依托 Starlette 异步核心与 Pydantic 数据验证模型,通过深度融合非阻塞 I/O 机制、自动化数据序列化及交互式 API 文档生成能力,可以显著降低微服务架构下的后端开发复杂度并提升系统可维护性。

拗口的话说完了,其实FastAPI = Starlette + Pydantic + 额外逻辑

功能模块 提供者 功能
路由、请求、响应 Starlette 处理 HTTP 协议、WebSocket、路由匹配、后台任务
数据验证、序列化 Pydantic 解析 JSON、类型检查、数据转换
自动文档、依赖注入 FastAPI 基于上述两者生成 接口API文档、依赖注入系统

如果学过Spring你一下就能发现其实Starlette类似就是干了Dispatcher和Handler的活,Pydantic干的就是HandlerAdapter的活。这里介绍的FastAPI主要涉及常用的FastAPI干活的全套功能,包括Pydantic+后续用于持久化的模块tortoise。

Fastapi处理请求的整个流程

完整的FastAPI处理请求的流程如下:

flowchart TD A[客户端发起请求] --> B[ASGI服务器<br>接收请求<br><small>ASGI = Asynchronous Server Gateway Interface(异步服务器网关接口)</small>] B --> C[FastAPI应用实例<br>预处理] C --> D[路由Router<br>匹配路径] D -- 匹配失败 --> E[返回404响应] D -- 匹配成功 --> F subgraph F [路径操作内部处理流水线] direction TB F1[依赖注入系统<br>解析依赖项] --> F2[请求体解析<br>Pydantic验证与转换] F2 --> F3[路径参数/查询参数<br>Pydantic验证与转换] F3 --> F4[用户自定义<br>路径操作函数] F4 --> F5[响应模型与序列化<br>Pydantic再次工作] end F -- 包含HTTPException --> E F -- 包含用户返回值 --> G subgraph G [响应生成] direction LR G1[默认JSONResponse] G2[自定义响应类] G3[异常处理器] G1 ~~~ G2 ~~~ G3 end E --> H[ASGI服务器<br>发送响应] G --> H H --> I[客户端接收响应] style F fill:#fffacd,stroke:#bdb76b style G fill:#fffacd,stroke:#bdb76b

阶段一:请求接收与预处理(ASGI 服务器 & FastAPI 应用)

1. ASGI 服务器接收请求:

  • 流程开始于 ASGI 服务器(一般就是Uvicorn)。服务启动时开始监听网络端口,接收来自客户端的原始 HTTP 请求。
  • ASGI 服务器将请求解析为一个 ASGI 连接“范围”(scope),其中包含了请求的方法、路径、请求头等基本信息。

2. FastAPI 应用实例接管:

  • ASGI 服务器调用 FastAPI 应用实例(一个符合 ASGI 规范的可调用对象),并将 scope、一个用于接收请求体的函数 receive 和一个用于发送响应的函数 send 传递给它。
  • FastAPI 基于 scope 创建一个 Request 对象(实际上是 Starlette 的 Request 对象)。这个对象封装了所有的请求信息。

简单类比JavaWeb:

控制反转 (IoC)

  • Tomcat: 你写好 Servlet,Tomcat 启动后,它调用你。
  • Uvicorn: 你写好 FastAPI,Uvicorn 启动后,它调用你。
  • 共同点: 开发者不需要写 while True 去监听 socket,容器负责底层网络,你只负责处理业务。

标准化接口

  • Servlet: 必须实现 javax.servlet.Servlet 接口。
  • ASGI: 必须是一个 async def app(scope, receive, send) 可调用对象。
  • 共同点: 只要符合规范,可以随意更换底层服务器(Tomcat ↔ Jetty,Uvicorn ↔ Hypercorn)。

请求封装

  • Servlet: req.getParameter()
  • ASGI: 通过 receive() 获取消息字典,FastAPI 再封装成 Request 对象给你用。

阶段二:请求路由匹配

路由器(Router)匹配路径:

  1. FastAPI 将请求交给内部的路由器(APIRouter)。
  2. 路由器根据请求的 URL 路径(request.url.path)和 HTTP 方法(如 GET),在所有已注册的路径操作中查找匹配项。
  3. 如果找不到匹配的路由,FastAPI 会自动处理并返回一个 404 Not Found 响应(一般可设置默认路由作为兜底)。

阶段三:路径操作内部处理流水线

一旦找到匹配的路径(例如 @app.get("/items/{item_id}")),请求就进入了该路径操作的核心处理流水线。这是最复杂也是最具特色的一步。

flowchart TD Start["✅ 路径匹配成功<br/>进入核心处理流水线"] --> Step1 subgraph Step1 [🔗 依赖注入系统] direction TB D1["检查路径操作声明的依赖项"] --> D2["按顺序执行依赖函数"] D2 --> D3["依赖返回值注入路径操作函数"] D2 -- 依赖抛出 HTTPException --> ExceptionHandle["⚠️ 跳转异常处理"] end Step1 --> Step2 subgraph Step2 [📦 请求体解析与验证] direction TB B1["检查是否定义 Pydantic 模型"] --> B2["异步读取请求体"] B2 --> B3["原始数据传递给 Pydantic"] B3 --> B4["验证字段类型和约束"] B4 -- 验证失败 --> VError["❌ ValidationError<br/>返回 422 Unprocessable Entity"] B4 -- 验证成功 --> B5["转换为 Python 对象<br/>如 JSON 字符串 123 → 整数 123"] end Step2 --> Step3 subgraph Step3 [🔑 路径/查询参数解析] direction TB P1["根据类型提示自动提取参数"] --> P2["路径参数 item_id: int"] P1 --> P3["查询参数 skip: int = 0"] P2 --> P4["类型转换与验证"] P3 --> P4 P4 -- 转换失败<br/>如 abc → int --> PError["❌ 返回 422 响应<br/>包含错误详情"] P4 -- 转换成功 --> P5["注入路径操作函数参数"] end Step3 --> Step4 subgraph Step4 [⚙️ 用户自定义路径操作函数] direction TB H1["接收已验证的参数"] --> H2["执行自定义业务逻辑"] H2 -- 抛出 HTTPException --> ExceptionHandle H2 -- 正常执行 --> H3["返回用户自定义值"] end Step4 --> Step5 subgraph Step5 [📤 响应模型与序列化] direction TB R1["Pydantic 再次工作"] --> R2["转换为 JSON 响应"] R2 --> R3["可自定义响应类覆盖"] end Step5 --> End["✅ 进入响应生成阶段"] style Start fill:#c8e6c9,stroke:#2e7d32 style End fill:#c8e6c9,stroke:#2e7d32 style VError fill:#ffebee,stroke:#f44336 style PError fill:#ffebee,stroke:#f44336 style ExceptionHandle fill:#fff3e0,stroke:#f57c00 style Step1 fill:#e3f2fd,stroke:#1976d2 style Step2 fill:#e3f2fd,stroke:#1976d2 style Step3 fill:#e3f2fd,stroke:#1976d2 style Step4 fill:#e3f2fd,stroke:#1976d2 style Step5 fill:#e3f2fd,stroke:#1976d2
"""
FastAPI 核心处理流水线 - 完整代码示例
对应流程图的每个环节都有注释说明
"""

from fastapi import FastAPI, HTTPException, Depends, Query, Path
from pydantic import BaseModel, Field, EmailStr
from typing import Optional

app = FastAPI()

# ============================================================================
# 📦 1. 定义 Pydantic 模型(用于请求体验证)
# ============================================================================

class ItemCreate(BaseModel):
    """请求体模型 - 对应流程图的【请求体解析与验证】"""
    name: str = Field(..., min_length=1, max_length=50, description="物品名称")
    price: float = Field(..., gt=0, description="价格必须大于 0")
    quantity: int = Field(default=1, ge=0, description="数量不能为负")
    owner_email: Optional[EmailStr] = None  # 自动验证邮箱格式

class ItemResponse(BaseModel):
    """响应模型 - 对应流程图的【响应模型与序列化】"""
    item_id: int
    name: str
    price: float
    quantity: int
    status: str = "active"

# ============================================================================
# 🔗 2. 定义依赖项(对应流程图的【依赖注入系统】)
# ============================================================================

async def verify_token(x_token: str = Header(...)) -> str:
    """依赖函数 1 - 验证 Token"""
    if x_token != "secret-token":
        raise HTTPException(status_code=401, detail="Invalid token")
    return x_token

async def get_db_session():
    """依赖函数 2 - 模拟数据库连接"""
    db = {"connection": "opened"}
    yield db  # 可以使用 yield,在请求结束后执行清理
    db["connection"] = "closed"

async def check_item_exists(item_id: int, db = Depends(get_db_session)):
    """依赖函数 3 - 检查物品是否存在"""
    if item_id > 100:  # 模拟不存在
        raise HTTPException(status_code=404, detail="Item not found")
    return {"id": item_id, "db": db}

# ============================================================================
# 🚀 3. 路径操作(完整展示处理流水线)
# ============================================================================

@app.post("/items/{item_id}", response_model=ItemResponse)
async def create_item(
    # --- 🔑 路径参数(自动验证)---
    item_id: int = Path(..., gt=0, description="物品 ID 必须大于 0"),
  
    # --- 🔍 查询参数(自动验证)---
    skip: int = Query(default=0, ge=0, description="跳过数量"),
    limit: int = Query(default=10, ge=1, le=100, description="限制数量"),
  
    # --- 📦 请求体(Pydantic 验证)---
    item: ItemCreate,
  
    # --- 🔗 依赖注入(按顺序执行)---
    token: str = Depends(verify_token),
    db: dict = Depends(get_db_session),
    item_exists: dict = Depends(check_item_exists),
):
    """
    路径操作函数 - 对应流程图的【用户自定义路径操作函数】
  
    此时所有参数已经过验证和转换:
    - item_id 已确认为 int 且 > 0
    - item 已确认为有效的 ItemCreate 对象
    - 依赖项已全部执行完成
    """
  
    # 模拟业务逻辑
    if item.price > 1000:
        raise HTTPException(status_code=400, detail="Price too high")
  
    # 返回数据(会自动被 Pydantic 序列化)
    return ItemResponse(
        item_id=item_id,
        name=item.name,
        price=item.price,
        quantity=item.quantity,
        status="active"
    )

# ============================================================================
# 🧪 4. 测试示例
# ============================================================================

"""
# 启动服务器
uvicorn main:app --reload

# 测试 1 - 成功请求 ✅
curl -X POST "http://localhost:8000/items/1?skip=0&limit=10" \
  -H "x-token: secret-token" \
  -H "Content-Type: application/json" \
  -d '{"name": "Apple", "price": 5.5, "quantity": 10}'

# 响应 200:
{
  "item_id": 1,
  "name": "Apple",
  "price": 5.5,
  "quantity": 10,
  "status": "active"
}

# 测试 2 - 验证失败 ❌ (price <= 0)
curl -X POST "http://localhost:8000/items/1" \
  -H "x-token: secret-token" \
  -H "Content-Type: application/json" \
  -d '{"name": "Apple", "price": -5, "quantity": 10}'

# 响应 422:
{
  "detail": [
    {
      "loc": ["body", "price"],
      "msg": "ensure this value is greater than 0",
      "type": "value_error.number.not_gt"
    }
  ]
}

# 测试 3 - 类型转换失败 ❌ (item_id 不是 int)
curl -X POST "http://localhost:8000/items/abc" \
  -H "x-token: secret-token" \
  -H "Content-Type: application/json" \
  -d '{"name": "Apple", "price": 5.5}'

# 响应 422:
{
  "detail": [
    {
      "loc": ["path", "item_id"],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ]
}

# 测试 4 - 依赖项失败 ❌ (Token 无效)
curl -X POST "http://localhost:8000/items/1" \
  -H "x-token: wrong-token" \
  -H "Content-Type: application/json" \
  -d '{"name": "Apple", "price": 5.5}'

# 响应 401:
{
  "detail": "Invalid token"
}

# 测试 5 - 依赖项失败 ❌ (物品不存在)
curl -X POST "http://localhost:8000/items/999" \
  -H "x-token: secret-token" \
  -H "Content-Type: application/json" \
  -d '{"name": "Apple", "price": 5.5}'

# 响应 404:
{
  "detail": "Item not found"
}
"""

1、依赖项注入处理:

  • FastAPI 首先检查该路径操作是否声明了任何“依赖项”(dependencies),如代码区域中依赖了verify_token相关函数。
  • 按顺序执行这些依赖函数。依赖项的返回值可以被注入到路径操作函数中。
  • 如果依赖项抛出 HTTPException,流程会立即中断,并跳转到异常处理部分。

2、请求体解析与验证:

  • 对于带有请求体(如 POST)的方法,FastAPI 会检查是否定义了 Pydantic 模型。
  • 异步读取:它会异步读取请求体。
  • 解析与验证:然后将读取到的原始数据(如 JSON)传递给对应的 Pydantic 模型。
  • 强大的类型转换与验证:
    • 验证:Pydantic 会检查数据是否符合模型中定义的字段类型和约束(例如,字符串格式、数字范围)。如果验证失败,Pydantic 会抛出 ValidationError,FastAPI 会自动将其转换为包含错误详情的 422 Unprocessable Entity 响应。
    • 转换:如果验证成功,Pydantic 会将原始数据转换为 Python 对象(例如,将 JSON 字符串 "123" 转换为整数 123)。这个对象就是路径操作函数中定义的参数。

3、路径/查询参数解析与验证:

  • 路径参数(如 /items/{item_id})和查询参数(如 ?skip=0&limit=10)也会经历类似的过程。
  • FastAPI 根据你在函数参数中声明的类型提示(如 item_id: int, skip: int = 0),自动从请求中提取这些参数,并尝试进行类型转换和验证。
  • 如果类型转换失败(例如,将 "abc" 转换为 int),FastAPI 会自动返回一个包含错误详情的 422 响应。

阶段四:路径操作函数(业务处理的函数)

执行至此,代码已执行到:

@app.post("/items/{item_id}", response_model=ItemResponse)
async def create_item(
    item_id: int,      # ✓ 已验证:int, >0
    skip: int,         # ✓ 已验证:int, >=0
    limit: int,        # ✓ 已验证:int, 1-100
    item: ItemCreate,  # ✓ 已验证:Pydantic 模型
    token: str,        # ✓ 依赖注入:已验证的 token
    db: dict,          # ✓ 依赖注入:DB 连接
    item_exists: dict, # ✓ 依赖注入:物品存在确认
):
    # 此时所有参数都是安全的 Python 对象!
    if item.price > 1000:
        raise HTTPException(status_code=400, detail="Price too high")
  
    return ItemResponse(
        item_id=item_id,
        name=item.name,
        price=item.price,
        quantity=item.quantity,
        status="active"
    )

1、现在,所有参数都已准备就绪:依赖项的返回值、已验证的请求体对象、已验证/转换后的路径和查询参数。
2、执行业务逻辑
3、生成返回值,将数据装入返回对象

阶段五:响应序列化(返回处理后结果)

1、Pydantic 序列化:判断是否是Pydantic类型,是则执行Pydantic定义的序列化函数完成校验及序列化
2、Json编码:将生成对象用Json.dumps打包编码
3、生成响应:创建响应对象,将响应数据放入对象,设置适当的状态码、响应头(如 Content-Type: application/json)等。
4、发送响应:

  • FastAPI 通过 ASGI 协议,调用 send 函数,将构建好的 HTTP 响应发送回 ASGI 服务器
  • ASGI 服务器最终将这个响应通过网络传回给客户端

错误处理

在整个流程中,任何地方都可能发生错误(路由不匹配、参数验证失败、依赖项出错、业务逻辑抛出异常等)。FastAPI 有一套完善的异常处理机制:

  • HTTPException:在代码中抛出 HTTPException,FastAPI 会捕获它并生成对应的 HTTP 错误响应。
  • 请求验证错误:由 Pydantic 或参数验证引起的 RequestValidationError 会被自动捕获,并生成包含详细错误信息的 422 响应。
  • 全局异常处理器:可以使用 @app.exception_handler() 来自定义如何处理特定类型的异常,提供友好的错误信息。

总结

FastAPI 的处理流程是一个高效、严谨的管道: ASGI 服务器 → FastAPI → 路由匹配 → 依赖项 → 参数/请求体验证(Pydantic)→ 路径操作函数 → 响应模型(Pydantic)→ 序列化 → 响应

其核心优势在于:

  • 深度集成 Pydantic:在请求入口和响应出口都进行了严格的类型验证和转换,保证了数据的可靠性和开发体验
  • 基于 ASGI:提供了异步支持,能够处理高并发 I/O 密集型请求
  • 清晰的依赖注入系统:使代码更模块化、可测试和可复用
  • 自动化文档生成:FastAPI 能自动生成准确的 OpenAPI 文档

项目整体结构

在对fastapi整体请求处理有一定基础之后,我们开始了解下FastAPI项目的一个开发结构

1. 顶层目录结构

project_root/
├── src/
│   └── app/                # 应用主目录
│       ├── core/           # 全局核心代码(配置、数据库、依赖等)
│       ├── modules/        # 业务模块目录,每个模块自成体系
│       ├── main.py         # FastAPI 应用入口
│       └── __init__.py
├── tests/                  # 测试代码
├── docs/                   # 项目文档
├── worker.py               # RQ (Redis Queue) worker 入口
├── Dockerfile              # Docker 镜像构建文件
├── docker-compose.yml      # Docker Compose 配置
├── pyproject.toml          # uv/Poetry 依赖管理文件
├── uv.lock                 # uv lock文件,锁定依赖版本
├── alembic.ini             # Alembic 配置(如用数据库迁移)
├── migrations/             # Alembic 迁移脚本目录
├── Makefile                # 常用命令脚本
├── .env.example            # 环境变量示例文件
├── .gitignore
└── README.md

2. APP目录结构

src/app/
├── core/
│   ├── config.py           # 配置加载与管理
│   ├── database.py         # 数据库连接与会话
│   ├── redis.py            # Redis 连接与 RQ 队列
│   ├── dependencies.py     # 全局依赖(如认证、权限)
│   ├── exceptions.py       # 自定义异常定义
│   ├── handlers.py         # 全局异常处理器
│   ├── security.py         # 安全相关工具(如密码哈希、JWT 编码解码)
│   ├── logging.py          # 日志配置与管理
│   └── __init__.py
│
├── modules/
│   ├── users/              # 用户模块
│   │   ├── router.py       # 路由定义(APIRouter)
│   │   ├── service.py      # 业务逻辑
│   │   ├── schemas.py      # Pydantic 数据模型 (请求/响应/校验)
│   │   ├── models.py       # SQLAlchemy ORM 数据模型 (数据库表结构)
│   │   ├── crud.py         # 数据库 CRUD 操作
│   │   ├── tasks.py        # 异步任务定义
│   │   └── __init__.py
│   │
│   ├── products/           # 产品模块
│   │   ├── router.py
│   │   ├── service.py
│   │   ├── schemas.py
│   │   ├── models.py
│   │   ├── crud.py
│   │   ├── tasks.py
│   │   └── __init__.py
│   │
│   └── ...                 # 其他业务模块
│
├── main.py                 # FastAPI 应用实例与路由注册
└── __init__.py

3. 配置文件介绍

对于一些敏感且不便硬编码的配置,比较pythonic的做法是用.env文件编辑管理,通过代码中的动态导入实现统一管理。
创建 .env文件
在项目根目录下创建 .env文件(xxx需替换为你的配置信息):

# FastAPI 连接数据库的URL
# docker使用
#DATABASE_URL=mysql+pymysql://root:xxx@host.docker.internal:3306/cognitive_disorder?charset=utf8mb4
# 本地使用
DATABASE_URL=mysql+pymysql://root:xxx@localhost:3306/cognitive_disorder?charset=utf8mb4

# minio配置(若需要使用minio对象存储)
MINIO_ENDPOINT=xxx
MINIO_ACCESS_KEY=xxx
MINIO_SECRET_KEY=xxx
MINIO_SECURE=false 
MINIO_BUCKET_NAME=xxx

# 若需要使用JWT验证,JWT配置
SECRET_KEY="miyue"
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=240

读取 .env文件
config.py文件中,通过Pydantic的 BaseSettings类实现。Pydantic 的 BaseSettings 是一个专门用于管理应用程序配置的类,它继承自 BaseModel,提供了强大的配置管理功能,特别适合处理环境变量和配置文件。

首先导入 BaseSettings类,在文件中定义一个类继承自 BaseSettings类,类的参数与 .env文件中的配置相对应,在类中使用 model_config = {"env_file": ".env", "case_sensitive": False, "extra": "ignore"}来加载 .env文件并为类中的参数赋值。接着我们创建一个类的实例,以后就可以使用这个实例来获取配置信息了。

pydantic-settings需要安装:

pip install pydantic-settings
from pydantic_settings import BaseSettings


class Settings(BaseSettings):
    # MySQL 数据库连接 URL
    DATABASE_URL: str
    # 告诉pydantic从.env读取
    model_config = {"env_file": ".env", "case_sensitive": False, "extra": "ignore"}


# 创建配置实例,供其他地方引用
settings = Settings()
tips:info 其实也可以用dotenv的库读取.env,功能稍微弱一点,比如无法设置忽略大小写,无法检测校验.env是否包含需要配置等

4. 项目启动配置

使用uv工具,可以快速创建一个基本项目基本结构并运行,接下来演示其过程:

  1. 初始化项目

    # 创建项目
    uv init fastapi-demo
    cd fastapi-demo
    
    # 安装依赖(添加 pydantic-settings 和 python-dotenv)
    uv add fastapi uvicorn[standard] pydantic-settings python-dotenv
    

    预期项目结构如下

fastapi-demo/
   ├── pyproject.toml
   ├── .env                    # 环境变量配置
   ├── .env.example            # 配置模板(提交到 Git)
   ├── src/
   │   └── fastapi_demo/
   │       ├── __init__.py
   │       ├── main.py         # 应用入口
   │       ├── config.py       # Pydantic 配置类 ⭐
   │       └── routes/
   │           └── __init__.py
   └── venv/

配置文件.env(不上传到git)

# .env
APP_NAME="My FastAPI App"
APP_VERSION="1.0.0"
DEBUG=true

# 服务器配置
HOST=0.0.0.0
PORT=8000

# API 配置
API_PREFIX=/api/v1
BASE_URL=http://localhost:8000

# 数据库配置(示例)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

# 其他配置
MAX_ITEMS=100
ALLOWED_HOSTS=["localhost", "127.0.0.1"]

配置文件.env.example(上传到git)

# .env.example
APP_NAME="My FastAPI App"
APP_VERSION="1.0.0"
DEBUG=false

# 服务器配置
HOST=0.0.0.0
PORT=8000

# API 配置
API_PREFIX=/api/v1
BASE_URL=http://localhost:8000

# 数据库配置
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

# 其他配置
MAX_ITEMS=100
ALLOWED_HOSTS=["localhost", "127.0.0.1"]

.gitignore

# .gitignore
.env
venv/
__pycache__/
*.pyc
.pytest_cache/
  1. 安装 FastAPI 和 Uvicorn
  2. 创建Pydatic配置类(读取和校验配置文件)
# src/fastapi_demo/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, HttpUrl, field_validator
from typing import List
from functools import lru_cache


class Settings(BaseSettings):
    """
    应用配置类 - 使用 Pydantic Settings 管理环境变量
    自动从 .env 文件读取配置,并进行类型验证
    """

    # ============ 应用基础配置 ============
    app_name: str = Field(default="FastAPI Demo", description="应用名称")
    app_version: str = Field(default="1.0.0", description="应用版本")
    debug: bool = Field(default=False, description="调试模式")
  
    # ============ 服务器配置 ============
    host: str = Field(default="0.0.0.0", description="服务器 host")
    port: int = Field(default=8000, ge=1, le=65535, description="服务器端口")
  
    # ============ API 配置 ============
    api_prefix: str = Field(default="/api/v1", description="API 前缀")
    base_url: HttpUrl = Field(default="http://localhost:8000", description="基础 URL")
  
    # ============ 数据库配置 ============
    database_url: str = Field(default="sqlite:///./test.db", description="数据库连接 URL")
  
    # ============ 其他配置 ============
    max_items: int = Field(default=100, ge=1, le=1000, description="最大物品数量")
    allowed_hosts: List[str] = Field(default=["localhost"], description="允许的主机列表")
  
    # ============ Pydantic Settings 配置 ============
    model_config = SettingsConfigDict(
        env_file=".env",           # .env 文件路径
        env_file_encoding="utf-8",  # 文件编码
        case_sensitive=False,       # 环境变量名不区分大小写
        extra="ignore",             # 忽略 .env 中未定义的字段
    )
  
    # ============ 自定义验证器 ============
    @field_validator("api_prefix")
    @classmethod
    def validate_api_prefix(cls, v: str) -> str:
        """确保 API 前缀以 / 开头"""
        if not v.startswith("/"):
            return f"/{v}"
        return v
  
    @property
    def docs_url(self) -> str:
        """文档 URL 属性"""
        return f"{self.base_url}/docs"
  
    @property
    def is_production(self) -> bool:
        """是否生产环境"""
        return not self.debug


@lru_cache()
def get_settings() -> Settings:
    """
    获取配置单例(使用 lru_cache 缓存,避免重复读取 .env)
    在 FastAPI 中作为依赖注入使用
    """
    return Settings()
  1. 创建其余基础代码
# src/fastapi_demo/main.py
from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from .config import Settings, get_settings


def create_app(settings: Settings) -> FastAPI:
    """
    应用工厂函数 - 使用配置创建 FastAPI 应用
    """
    # 创建 FastAPI 实例,使用配置中的信息
    app = FastAPI(
        title=settings.app_name,
        version=settings.app_version,
        debug=settings.debug,
        description=f"""
        ## {settings.app_name}
    
        **版本**: {settings.app_version}
    
        **基础 URL**: {settings.base_url}
    
        ### 特性
        - 自动配置加载
        - Pydantic 类型验证
        - 环境变量管理
        """,
    )
  
    # ============ 添加中间件 ============
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.allowed_hosts,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
  
    # ============ 注册路由 ============
    @app.get("/")
    async def root(config: Settings = Depends(get_settings)):
        """根路径 - 显示应用信息"""
        return {
            "app_name": config.app_name,
            "version": config.app_version,
            "debug": config.debug,
            "docs_url": config.docs_url,
        }
  
    @app.get("/health")
    async def health_check(config: Settings = Depends(get_settings)):
        """健康检查"""
        return {
            "status": "healthy",
            "environment": "production" if config.is_production else "development",
        }
  
    @app.get(f"{settings.api_prefix}/items/{{item_id}}")
    async def get_item(
        item_id: int,
        limit: int = 10,
        config: Settings = Depends(get_settings)
    ):
        """获取物品 - 使用 API 前缀"""
        if limit > config.max_items:
            raise HTTPException(
                status_code=400,
                detail=f"Limit cannot exceed {config.max_items}"
            )
        return {
            "item_id": item_id,
            "limit": limit,
            "max_allowed": config.max_items,
            "base_url": str(config.base_url),
        }
  
    @app.get("/config")
    async def get_config(config: Settings = Depends(get_settings)):
        """
        查看当前配置(生产环境建议禁用)
        """
        if config.is_production:
            raise HTTPException(
                status_code=403,
                detail="Config endpoint disabled in production"
            )
        # 不返回敏感信息
        return {
            "app_name": config.app_name,
            "app_version": config.app_version,
            "debug": config.debug,
            "host": config.host,
            "port": config.port,
            "api_prefix": config.api_prefix,
            "max_items": config.max_items,
        }
  
    return app


# 创建应用实例
settings = get_settings()
app = create_app(settings)


if __name__ == "__main__":
    import uvicorn
    # 从配置读取 host 和 port
    settings = get_settings()
    print("==========================================")
    print(settings)
    uvicorn.run(
        "src.fastapi_demo.main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.debug
    )
  1. 启动服务器运行

方式一:使用 uv run(推荐)

# 开发模式(自动重载)
uv run uvicorn src.fastapi_demo.main:app --reload
# 或者使用 main.py 中的配置启动
uv run python -m src.fastapi_demo.main

方式二:直接使用 uvicorn

# 指定 host 和 port(会覆盖 .env 中的配置)
uv run uvicorn src.fastapi_demo.main:app --reload --host 0.0.0.0 --port 8000
tips:warn 由于采用code-server开发,访问时使用了其提供的/proxy/8000/所以需要使用root-path主动修改访问路径地址,有类似需要可参考 uv run uvicorn src.fastapi_demo.main:app \ --reload \ --host 0.0.0.0 \ --port 8000 \ --root-path /proxy/8000
  1. 测试访问结果
    测试访问 localhost:8000/
{"app_name":"My FastAPI App","version":"1.0.0","debug":true,"docs_url":"http://localhost:8000//docs"}

启动项目与自动生成的接口文档

fastapi项目默认会生成api文档页面,假设访问项目地址为 localhost:8000/,则只需访问localhost:8000/docs即可访问api文档地址,访问localhost:8000/openapi.json可获取json格式文档

FastAPI学习总结(一)
Author
Administrator
Published at
2026-02-24
License
CC BY-NC-SA 4.0

Comment