尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

FastAPI零基础实战:从路由到部署完整指南

FastAPI零基础实战:从路由到部署完整指南 这次我们直接看 FastAPI。如果你写 Python迟早要写接口、做后端、把模型封装成服务FastAPI 是目前上手成本最低、文档最省心、性能也够用的那一档。跟 Flask 比它自带请求参数校验、自动生成接口文档、异步支持省掉大量手写校验和文档维护的功夫跟 Django 比它轻量没有繁琐的项目脚手架一个文件就能跑起一个服务。今天这篇就从零开始把一个 FastAPI 项目从环境准备到部署上线完整过一遍重点包括路由、路径参数、Pydantic 请求体、响应模型、统一返回格式、Union 类型、依赖注入、权限管理、中间件、自动 API 文档以及最常见的“把本地模型封装成 HTTP 接口”场景。看完之后你应该能自己搭一个能跑、能调、能上线的接口服务。这篇文章是给零基础读者写的不要求你有 Flask 或 Django 经验只需要会最基本的 Python 语法比如函数、类、列表和字典。如果你之前只用 Jupyter Notebook 写过数据处理脚本也可以照着做代码量不大每一步都有可复制的命令和示例。重点不是把 FastAPI 的每个特性背下来而是先跑通一个最小服务再逐步加参数校验、加权限控制、加统一返回格式最后接到你的业务里。1. FastAPI 核心能力速览能力项说明项目类型Python Web 框架用于构建 HTTP 接口服务开发语言Python官方要求 3.8建议使用 3.10 或更高版本安装方式pip 安装 fastapi uvicorn无需额外编译路由功能支持 GET、POST、PUT、DELETE、PATCH 等常用 HTTP 方法参数处理路径参数、查询参数、请求体、请求头、Cookie、表单数据校验基于 Pydantic自动校验请求字段类型与范围返回格式支持 response_model 统一输出可自定义 ApiResponse 模型权限控制依赖注入 Header 校验 APIRouter 路由级拦截自动文档内置 Swagger UI/docs和 ReDoc/redoc异步支持原生 async/await也支持普通 def 同步函数批量任务可借助 BackgroundTasks 或外部队列实现本教程会给出基础用法适合场景后端 API、模型封装、微服务、前后端分离项目、本地工具服务这套能力对零基础用户最友好的地方在于不需要写一堆装饰器之外的框架代码你定义函数FastAPI 负责把 HTTP 请求转换成 Python 参数再帮你把返回值转成 JSON。如果你之前被 Flask 的手动请求解析和参数校验折磨过FastAPI 的体验会明显轻松很多。2. 适用场景与使用边界FastAPI 适合三类场景。第一类是纯后端 API。前端 Vue、React或者小程序、App通过 HTTP 请求访问你的接口FastAPI 返回 JSON 数据。第二类是算法和模型封装。你把一个图像分类模型、OCR 模型、文本生成模型、语音识别模型或者 RAG 知识库问答流程包成一个服务输入文本或者文件输出结构化结果。第三类是内部工具和自动化平台比如给团队做一个数据查询接口、定时任务管理后台、批量处理任务网关。使用边界也要说清楚。FastAPI 本身不提供数据库 ORM需要配合 SQLAlchemy、SQLModel 或 Tortoise-ORM 使用也不像 Django 自带 Admin 后台需要自己写管理页面对于页面渲染虽然可以用 Jinja2 做服务端渲染但那不是 FastAPI 的主场它更适合做纯 API 服务。在模型封装和数据服务场景里还要注意合规使用。如果你把图片、文档、人脸、声音、文本等数据传给模型接口处理必须确认数据和素材来源合法涉及个人肖像、声音、版权文本时需要获得明确授权尤其是把服务部署到公网时要先加权限控制再开放访问。本地测试时建议仅监听 127.0.0.1避免接口暴露到局域网或公网。3. 环境准备与前置条件开始之前先准备三样东西Python、虚拟环境和一个编辑器。3.1 Python 版本确认FastAPI 官方要求 Python 3.8 及以上。不过考虑到类型注解的便利性我建议使用 Python 3.10 或更高版本这样可以直接用str | None这种简化写法而不是写Union[str, None]。确认版本python --version如果你的系统里同时装了多个 Python可以用python3命令确认。Windows 用户如果提示找不到命令可以检查是否勾选了“Add Python to PATH”或者使用安装的完整路径。3.2 创建虚拟环境每个项目单独建虚拟环境避免依赖版本互相污染。这是零基础最容易忽略的一步也是后面“为什么我这跑不起来”最常见的根因。Windows 下执行python -m venv venv venv\Scripts\activatemacOS 或 Linux 下执行python3 -m venv venv source venv/bin/activate激活后命令行提示符前面会多出一个(venv)说明当前已经进入虚拟环境。3.3 编辑器选择推荐使用 VS Code装好 Python 扩展后代码补全和类型提示体验很好。如果你习惯 PyCharm也完全可以新建项目时选择已有的虚拟环境路径就行。对于零基础来说VS Code 轻量一些启动快不用等索引。4. 安装 FastAPI 与启动第一个服务4.1 安装依赖FastAPI 真正干活需要两个库fastapi提供框架能力uvicorn负责启动一个 ASGI 服务器。安装命令pip install fastapi pip install uvicorn[standard]uvicorn[standard]会额外安装一些性能相关的依赖比如uvloop、websockets如果你用默认的uvicorn也可以跑但推荐直接装带标准扩展的版本。4.2 编写最小服务新建一个main.py写入下面的代码from fastapi import FastAPI app FastAPI() app.get(/) def read_root(): return {message: Hello FastAPI}这里做了一件事创建 FastAPI 实例app然后定义了一个处理GET /请求的函数。函数返回值是一个字典FastAPI 会自动把它转成 JSON 响应。4.3 启动服务在终端执行uvicorn main:app --reloadmain:app表示从main.py文件中导入名为app的对象--reload表示代码文件修改后自动重启服务开发时非常方便。启动后终端会显示Uvicorn running on http://127.0.0.1:8000。浏览器打开http://127.0.0.1:8000你应该能看到{message: Hello FastAPI}到这里你的第一个 FastAPI 服务已经跑起来了。这个流程适合所有后续接口开发后面的所有代码都写在main.py里保存后刷新页面--reload会自动加载新代码。5. 路由与路径参数从 GET 到动态路径5.1 常见 HTTP 方法与路由注册FastAPI 用装饰器注册路由。基础写法是from fastapi import FastAPI app FastAPI() app.get(/items) def get_items(): return {method: GET} app.post(/items) def create_item(): return {method: POST} app.put(/items) def update_item(): return {method: PUT} app.delete(/items) def delete_item(): return {method: DELETE}每个装饰器对应一个 HTTP 方法函数名可以随便取真正决定访问路径的是装饰器里的字符串参数。5.2 路径参数路径参数是 URL 地址中动态变化的那一部分。比如获取某个用户的信息路径可以设计成/users/1、/users/2这里的1、2就是路径参数。app.get(/users/{user_id}) def get_user(user_id: int): return {user_id: user_id, name: fuser_{user_id}}注意user_id: int这个类型注解。FastAPI 会根据它做两件事第一把 URL 里的字符串自动转成 int 类型第二如果访问/users/abc自动返回 422 参数校验错误而不是等到函数内部报异常。这就是 FastAPI 和 Flask 最大的区别之一参数校验是声明式的你写类型它帮你做校验不需要手动写isinstance()判断。5.3 查询参数查询参数是 URL 问号后面的部分。比如分页接口/items?skip0limit10这里的skip和limit就是查询参数。app.get(/items) def list_items(skip: int 0, limit: int 10): return {skip: skip, limit: limit}带默认值的参数表示调用方可以不传不带默认值的参数必须传否则会返回 422 错误。app.get(/search) def search(q: str): return {query: q}访问/search?qfastapi返回{query: fastapi}访问/search会返回校验失败。6. 请求体与 Pydantic 模型POST、PUT 请求通常需要携带请求体。FastAPI 的处理方式是定义一个 Pydantic 模型然后用它作为函数参数的类型注解。from pydantic import BaseModel class Item(BaseModel): name: str price: float is_offer: bool False app.post(/items) def create_item(item: Item): return {name: item.name, price: item.price, is_offer: item.is_offer}6.1 请求体验证当你向/items发送 POST 请求时FastAPI 会读取请求体内的 JSON 数据按照Item模型做类型校验。下面这个请求是合法的{ name: 键盘, price: 199.0 }is_offer有默认值False所以不传也可以。如果传入price: abcFastAPI 会返回 422 错误告诉你price字段期望一个 float 类型。这种校验能力在真实项目中非常关键。没有框架帮你做校验时每个接口都要自己判断字段是否存在、类型对不对、范围合理不合理代码写起来又长又容易漏。Pydantic 模型让这部分变成声明式配置。6.2 字段约束Pydantic 不只校验类型还可以通过Field控制长度和范围from pydantic import BaseModel, Field class Item(BaseModel): name: str Field(..., min_length1, max_length50) price: float Field(..., gt0, le100000)...表示这个字段必填。min_length、max_length控制字符串长度gt表示大于 0le表示小于等于 100000。这些约束同样会在请求进入函数前自动校验。7. 统一返回格式与 response_model实际项目中如果一个接口返回{code: 0, message: success, data: ...}另一个接口直接返回原始数据前端代码会非常痛苦。统一返回格式是 FastAPI 工程化里很基础也很重要的一步。7.1 定义统一返回模型先定义一个通用的响应模型from typing import Any from pydantic import BaseModel class ApiResponse(BaseModel): code: int 0 message: str success data: Any Nonedata使用Any类型表示可以放任意数据这样无论是字典、列表还是字符串都能放进这个模型。7.2 接口里返回统一结构app.post(/items, response_modelApiResponse) def create_item(item: Item): saved {name: item.name, price: item.price} return ApiResponse(datasaved)这样接口返回的 JSON 结构就固定为{ code: 0, message: success, data: { name: 键盘, price: 199.0 } }7.3 失败时返回统一错误还需要一个失败分支。比如参数校验通过了但业务逻辑判断数据不合法这时候可以抛HTTPException或者返回自定义的错误码from fastapi import HTTPException app.get(/items/{item_id}, response_modelApiResponse) def get_item(item_id: int): if item_id 0: raise HTTPException(status_code400, detailitem_id must be positive) return ApiResponse(data{item_id: item_id})HTTPException适合处理需要自动生成错误状态码的场景。如果你希望前端误码逻辑简单也可以自己定义一个错误响应函数统一返回code非 0 的结构比如def error_response(message: str, code: int 1): return ApiResponse(codecode, messagemessage, dataNone)统一返回格式的核心价值是让前后端对接的契约稳定下来特别是当后端接口由多个同事维护时所有接口遵循同一个结构前端可以封装一个统一的请求器省掉大量重复判断逻辑。8. Union 与 Optional接口参数类型怎么写FastAPI 相关教程里经常出现Union和Optional搜索引擎里也经常有人搜“fastapi union作用”。这里单独说明。8.1 允许参数为 None最常见的场景是某个查询参数可以不传也可以传。比如搜索接口用户可能没输入关键词。from typing import Union app.get(/search) def search(q: Union[str, None] None): return {query: q}Union[str, None]表示这个参数的类型可以是str也可以是None。不传时FastAPI 会把它当成None。Python 3.10 及以上版本可以简写成app.get(/search) def search(q: str | None None): return {query: q}两种写法效果一样。8.2 Optional 与 Union 的关系早期 FastAPI 教程里经常写Optional[str]它的本质就是Union[str, None]。但更准确的表达是Optional只表示“允许为空”不表示“有默认值”。真正让参数可以不传的是后面的 None。所以推荐直接用str | None None语义更直白。8.3 Union 的另一个用途多类型参数Union也可以用在请求体模型字段上class Config(BaseModel): timeout: Union[int, float] 1.0 retries: Union[int, None] None这个能力在对接第三方接口、模型参数配置里很常见因为外部传进来的配置经常存在多种合法类型。9. 依赖注入与权限管理FastAPI 的权限管理核心机制是依赖注入Depends。简单说你定义一个函数负责校验权限然后在接口函数中声明依赖FastAPI 会在执行接口函数前先执行校验函数。9.1 一个最简单的 Token 校验from fastapi import Depends, Header, HTTPException def verify_token(authorization: str Header(...)): if authorization ! secret-token: raise HTTPException(status_code401, detailunauthorized) return authorization app.get(/secure) def secure_endpoint(token: str Depends(verify_token)): return {status: ok}请求/secure时如果不带Authorization请求头会返回 422如果带了Authorization: secret-token就会正常返回{status: ok}。9.2 使用 APIRouter 做路由级权限控制项目变大后不可能给每个接口都手动加Depends。更好的做法是按模块拆分路由然后用APIRouter统一加权限依赖。先创建一个routers包目录结构如下project/ ├── main.py └── routers/ ├── __init__.py └── admin.pyadmin.pyfrom fastapi import APIRouter, Depends router APIRouter(prefix/admin, dependencies[Depends(verify_token)]) router.get(/stats) def get_stats(): return {users: 100}main.py注册路由from fastapi import FastAPI from routers.admin import router as admin_router app FastAPI() app.include_router(admin_router)这样/admin/stats会自动执行verify_token校验不需要在接口函数里重复编写。9.3 更细的权限拆分真实项目里可以用依赖叠加实现不同权限等级。比如先校验是否登录再校验是否有管理员权限def verify_admin(token: str Depends(verify_token)): if token ! admin-token: raise HTTPException(status_code403, detailforbidden) return token router.get(/admin-only, dependencies[Depends(verify_admin)]) def admin_only(): return {secret: admin data}这种依赖注入的设计比在接口函数内部手写权限判断清晰得多权限逻辑可以复用测试也更好写。10. 中间件与 CORS 配置10.1 自定义 HTTP 中间件中间件的作用是在请求进入路由函数之前、响应返回给客户端之前插入一些统一处理逻辑比如日志记录、耗时统计、请求 ID 注入。import time from fastapi import Request app.middleware(http) async def add_process_time_header(request: Request, call_next): start time.time() response await call_next(request) process_time time.time() - start response.headers[X-Process-Time] str(round(process_time, 4)) return response这里call_next(request)会继续往下执行实际的接口函数拿到响应后给响应头添加了一个X-Process-Time字段记录请求耗时。这样前端和排查日志时都能看到每个接口的响应时间。10.2 CORS 配置如果你的前端页面运行在http://localhost:3000后端接口运行在http://localhost:8000浏览器默认会拦截跨域请求导致前端无法调接口。FastAPI 通过CORSMiddleware解决from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins[http://localhost:3000], allow_credentialsTrue, allow_methods[*], allow_headers[*], )开发阶段可以直接用allow_origins[*]表示允许所有来源访问。但部署到公网时建议把allow_origins限制为你自己的前端域名避免接口被其他站点任意调用。10.3 中间件执行顺序FastAPI 的中间件是嵌套执行的先注册的先处理请求。多个中间件之间的顺序会影响逻辑比如 CORS 中间件应该尽量早注册确保预检请求能正常通过。11. 自动 API 文档与接口调试FastAPI 内置了 Swagger UI 和 ReDoc启动服务后直接访问/docs和/redoc就能看到。11.1 Swagger UI启动uvicorn main:app --reload后浏览器访问http://127.0.0.1:8000/docs页面上会列出所有已注册的路由、HTTP 方法、请求参数、请求体模型和响应模型。点击某个接口可以直接填入参数并点击 Execute 发起真实请求FastAPI 会展示完整的请求 URL、请求体和响应结果。这个能力在零基础入门阶段尤其有用。你不需要提前准备 Postman 或 curl只要服务跑起来就能在/docs页面对每个接口做测试。11.2 OpenAPI 导出FastAPI 的自动文档基于 OpenAPI 规范接口结构会生成一份标准的 JSON 描述文件默认路径http://127.0.0.1:8000/openapi.json这份文件可以被 Postman、Apifox 等工具导入也可以用于生成客户端 SDK。如果团队要求接口文档统一管理FastAPI 服务本身就是文档源不需要额外维护。11.3 给接口加描述信息给接口函数写 docstring 和参数描述文档会更清晰app.get(/items/{item_id}) def get_item(item_id: int): 根据 ID 获取商品信息。 - item_id: 商品 ID必须为正整数 return {item_id: item_id}docstring 会显示在/docs页面的接口说明里。零基础阶段不要求写得多规范但建议从第一天开始养成注释习惯后面接口多了会省很多沟通成本。12. 实战用 FastAPI 封装本地模型服务FastAPI 在 AI 场景里最常见的用法之一就是把视觉模型、文本模型、RAG 知识库问答等封装成 HTTP 接口。这里给出一个通用思路你用同样的套路可以套到自己的模型上。12.1 同步推理接口假设你有一个 OCR 函数输入是一张图片的 base64 字符串输出是识别文本。可以这样封装import base64 from pydantic import BaseModel from fastapi import FastAPI app FastAPI() class OCRInput(BaseModel): image_base64: str def run_ocr(image_base64: str) - str: # 这里调用你自己的 OCR 模型 # 示例解码图片 - 模型推理 - 返回文本 return 识别结果 app.post(/ocr, response_modelApiResponse) def ocr_api(req: OCRInput): try: result run_ocr(req.image_base64) return ApiResponse(data{text: result}) except Exception as e: return ApiResponse(code1, messagestr(e), dataNone)关键点有三个第一请求体用 Pydantic 模型约束输入第二模型推理逻辑单独抽成run_ocr函数方便测试第三异常捕获后在统一返回格式里返回错误信息而不是让服务直接崩溃。12.2 长耗时任务的 BackgroundTasks如果模型推理需要十几秒甚至几分钟HTTP 请求会长时间阻塞。这时候可以用BackgroundTasks先把任务接收下来再异步处理前端通过任务 ID 轮询结果。from fastapi import BackgroundTasks from pydantic import BaseModel class TaskRequest(BaseModel): input_text: str task_store {} def process_task(task_id: str, input_text: str): # 模拟耗时推理 result input_text.upper() task_store[task_id] result app.post(/task) def create_task(req: TaskRequest, background_tasks: BackgroundTasks): import uuid task_id str(uuid.uuid4()) background_tasks.add_task(process_task, task_id, req.input_text) return {task_id: task_id, status: pending} app.get(/task/{task_id}) def get_task(task_id: str): if task_id in task_store: return {task_id: task_id, status: done, result: task_store[task_id]} return {task_id: task_id, status: pending}这种“提交任务 轮询结果”的模式在视觉模型封装、RAG 知识库问答、批量文本生成里非常常用。BackgroundTasks适合单机轻量场景如果任务量很大需要换成 Celery 或 Redis 队列但接口设计思路是一样的。12.3 配置参数通过查询参数传递模型推理的 batch size、温度、最大长度等参数可以通过查询参数或请求体传入class GenerateInput(BaseModel): prompt: str temperature: float 0.7 max_tokens: int 512这样同一个接口可以适配多种推理配置调用方按需调整。13. 生产部署uvicorn 多进程与 Docker开发时用--reload生产环境不能这样跑。生产部署需要关掉自动重载、绑定对外地址、启动多个 worker。13.1 多进程启动uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4--workers 4表示启动 4 个 worker 进程能利用多核 CPU 提高并发能力。需要注意的是--reload和--workers不能同时使用开发用--reload生产用--workers。13.2 Docker 部署写一个 DockerfileFROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8000]requirements.txt内容fastapi uvicorn[standard]构建和运行docker build -t fastapi-demo . docker run -d -p 8000:8000 fastapi-demo启动后访问http://localhost:8000和http://localhost:8000/docs效果跟本地运行一致。13.3 部署时要注意的点生产部署前要检查 CORS 白名单、接口鉴权是否生效、是否开启了文档站点、日志输出是否完整。如果接口涉及隐私数据建议在反向代理层启用 HTTPS并把 Swagger 文档限制在内网访问避免接口定义和数据结构直接暴露到公网。14. 常见问题与排查方法问题现象可能原因排查方式解决方案启动报错ModuleNotFoundError: No module named fastapi虚拟环境未激活或未安装依赖执行pip list检查 fastapi 是否存在激活虚拟环境后执行pip install fastapi启动报端口被占用8000 端口已被其他进程占用查看日志“address already in use”换端口启动uvicorn main:app --reload --port 8001修改代码后服务不生效启动时没加--reload确认启动命令参数开发时加上--reload或手动重启服务访问接口返回 422请求参数缺失或类型不匹配在/docs页面使用 Execute 测试检查字段名、类型是否与 Pydantic 模型一致前端跨域请求失败后端未配置 CORS查看浏览器控制台错误信息配置CORSMiddleware限制允许的来源请求体字段是合法的但仍校验失败字段名不一致或使用了下划线/驼峰混用查看/docs中请求体示例保持前后端字段命名一致/docs页面打不开服务未启动或端口不对curl 访问/openapi.json确认 uvicorn 正常启动、端口未冲突部署后只有容器内能访问容器端口未映射检查docker ps端口映射运行容器时加-p 8000:8000同步接口阻塞请求特别慢模型推理耗时较长未使用异步或后台任务观察接口响应时间按需改用async def或BackgroundTasksUnion类型接口直接报错Python 版本较低无法解析新语法执行python --version升级 Python 3.10或用typing.Union写法最重要的一条排查思路FastAPI 的报错信息通常比较明确先看终端日志再看/docs页面有没有正确显示接口最后看请求响应体里的具体错误字段。大部分 422 错误都能从响应体的detail字段直接定位到哪个参数不对。15. 最佳实践与学习建议最后给零基础读者几条实践建议。第一先跑通最小示例再逐步加功能。不要一开始就打算写一个完整系统先跑Hello FastAPI再加路径参数再加请求体最后引入权限和数据库。每加一个功能都在/docs页面验证一次。第二从第一天起使用统一返回格式。哪怕只是一个 demo也建议定义ApiResponse模型。后期接口变多后统一返回格式能省掉大改前端的麻烦。第三路径、参数、模型命名保持与业务语义一致。接口路径建议使用名词复数形式比如/users、/items请求体和响应体字段使用小写字母加下划线前后端约定一致避免在接口层做大量字段映射。第四权限控制要早加。即使只是本地开发也可以通过 Header 写一个简单的 token 校验习惯后进入真实项目时会少踩很多坑。注意 token 不要硬编码在代码里建议通过环境变量或配置文件读取。第五日志和异常处理要有。接口函数内部不要把所有异常都吞掉至少要打印 traceback或者返回统一错误结构。部署时把--reload关掉使用--workers多进程启动。接下来你可以准备两件事第一把这篇教程里的代码全部手敲一遍再自己加一个/health健康检查接口和/version版本号接口试试路径参数和查询参数的综合用法第二如果有正在训练的模型试着用POST /predict把它封装成 HTTP 接口前端调用一下体会一下模型到服务的完整链路。学完这些基础下一步就是接 SQLAlchemy 操作数据库、用 Pydantic Settings 管理配置、写单元测试这些都可以一步一个坑慢慢来。
返回列表