
这两年 FastAPI 在 Python 后端圈子里基本成了“标配”VSCode 又是绝大多数 Python 开发者手边最顺手的编辑器。两者的组合用好了开发体验是真的能直线拉升——启动快、自动生成接口文档、自带类型校验配合 VSCode 的 Python 插件和断点调试本地写一个后端服务就像写脚本一样轻松。这篇文章我想把“VSCode 中创建并运行 FastAPI 项目”这件事完完整整地拆开讲一遍从环境准备、虚拟环境创建、目录结构设计到在 VSCode 里写第一个接口、跑起来、打断点再到常见问题的排查实录最后顺手加上一个现在很多人都在问的“FastAPI 调用本地大模型”的小案例。不管你是刚接触 Python 后端的新手还是从 Flask/Django 转过来的老手按这套流程走一遍基本能把日常开发需要的每个环节都理顺。1. 准备阶段把开发环境一次配到位别让工具拖后腿1.1 为什么是这个组合VSCode 和 FastAPI 各自解决了什么问题很多人问为什么偏偏是 VSCode 配 FastAPI而不是 PyCharm 配 Django。我的观点是FastAPI 的设计理念和 VSCode 的定位其实非常搭。FastAPI 本身的“轻”体现在几个地方——它基于 ASGI原生支持异步性能上比 Flask 高不少它依赖 Python 的类型注解来做参数校验和序列化写起来代码量比 Django REST Framework 少一大截最省心的是它自带交互式 API 文档/docs前端联调、自测都不需要额外工具。这意味着你在本地只需要一个轻量编辑器就能很舒服地完成所有开发工作没必要为一个小项目拖起一个重型 IDE。VSCode 的“轻”则体现在启动速度和插件生态上。它不像 PyCharm 那样需要漫长的索引过程打开一个 FastAPI 项目几秒钟就能进入编辑状态配合 Python、Pylance 两个插件代码补全、类型提示、静态检查都能拉满。加上它内置的终端和调试器等于一个界面里同时解决了“写代码、跑服务、调试”三件事。1.2 需要准备的组件清单少装一个都会在后面的步骤里卡住这里直接给一份我在新机器上安装时实际执行的组件清单按顺序装就好组件版本建议说明VSCode最新稳定版尽量从官网下载社区版完全够用Python3.10 或 3.11FastAPI 要求 3.8但 3.10 以上的类型提示体验更完整pip随 Python 自带后面安装 FastAPI 和 uvicorn 用Pylance 插件VSCode 扩展市场安装提供类型检查和智能补全没有它体验打五折Python 插件VSCode 扩展市场安装解释器选择、调试配置都靠它Python 版本这块我多说一句。虽然 FastAPI 官方声明支持 3.8 以上但 PydanticFastAPI 底层的数据校验库在 3.10 以上版本里的报错信息要友好很多。我遇到过不少人在 3.8 环境里被 Pydantic 的隐式类型转换坑过升级到 3.11 之后问题自动消失。所以如果你是全新安装直接选 3.11 或更高版本省心。安装完 VSCode 之后记得打开扩展面板搜索 “Python” 安装微软官方插件再安装 “Pylance”。这两个插件会联动工作选好解释器之后VSCode 会自动根据你的代码推断类型写 FastAPI 路由的时候会有非常好的补全体验。1.3 创建虚拟环境为什么必须在项目里用 venv很多新手会把包直接装到全局 Python 环境里当时觉得方便后面项目多了就会踩到依赖冲突的坑——A 项目要用 fastapi 0.95B 项目要用 fastapi 0.104放在全局环境里就是一场灾难。虚拟环境的价值就是把项目的依赖完全隔离一个项目一个环境互不干扰。在项目文件夹里执行下面的命令python -m venv venv这条命令会在当前目录下创建一个名为venv的文件夹里面装着一个独立的 Python 解释器和 pip。接下来激活它Windows PowerShell 下执行venv\Scripts\Activate.ps1如果上面这条命令提示“禁止运行脚本”用下面这条绕过当前会话的限制Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUsermacOS 或 Linux 下执行source venv/bin/activate激活之后命令行提示符前面会出现(venv)前缀就说明你已经处于虚拟环境里了。接下来安装 FastAPI 和 uvicornpip install fastapi uvicorn这里解释一下为什么要装 uvicorn。FastAPI 本身只是一个框架它不包含运行服务器真正负责接收 HTTP 请求、把请求交给 FastAPI 处理、再把响应返回给客户端的是 uvicorn。所以这两个必须同时装。为了后面的调试更方便建议顺手再装一个httpx这是 FastAPI 官方推荐的测试客户端依赖后面跑接口测试会用到pip install httpx2. 项目结构设计别把所有代码塞进一个 main.py2.1 从零搭建一套可扩展的目录结构第一次接触 FastAPI 的人最常见的情况是所有的路由都写在main.py一个文件里项目一大了文件动辄上千行改一个接口要在代码里翻半天。这里我直接给出一个我实际项目里在用的基础目录结构按这个套路走后面扩展会很舒服my_fastapi_project/ ├── .venv/ # 虚拟环境目录 ├── app/ # 应用主目录 │ ├── __init__.py │ ├── main.py # 创建 FastAPI 实例注册路由 │ ├── routers/ # 按业务拆分的路由模块 │ │ ├── __init__.py │ │ ├── users.py # 用户相关接口 │ │ └── items.py # 商品相关接口 │ ├── schemas/ # Pydantic 数据模型请求/响应格式 │ │ ├── __init__.py │ │ └── user.py │ ├── models/ # 数据库模型 │ │ └── __init__.py │ └── core/ # 配置、依赖、工具函数 │ └── config.py ├── requirements.txt # 依赖清单 └── .gitignore # 忽略 venv 和缓存文件你不需要一上来就建这么多目录但如果项目预期会超过三五个接口我建议一开始就按这个思路拆不要后面再重构。2.2 最小可运行项目main.py 里到底该写什么在app/main.py里写一个最小可运行的 FastAPI 应用from fastapi import FastAPI app FastAPI(titleMy FastAPI Project) app.get(/) def read_root(): return {message: Hello, FastAPI!} app.get(/health) def health_check(): return {status: ok}这段代码里有几个点值得展开说明。FastAPI(titleMy FastAPI Project)这个title参数不是随便填的它会在自动生成的/docs文档页面上展示为文档标题。如果你后面要给前端同事或者测试人员看接口文档这个字段能让他们一眼识别出是哪个服务。app.get(/)是路由装饰器的核心用法。app.get表示这个方法处理 HTTP GET 请求括号里的/是请求路径。FastAPI 还支持app.post、app.put、app.delete等其他 HTTP 方法分别对应增删改查操作。在read_root函数上写def就行不需要函数体里有任何特殊语法返回一个 Python 字典时FastAPI 会自动把它序列化成 JSON 格式的响应。很多人第一次看到 FastAPI 的代码会疑惑为什么函数返回值不是 JSON 字符串也能被 API 返回这其实是 FastAPI 的内部机制——它自动检测你函数的返回类型通过 Pydantic 把兼容类型序列化为 JSON 响应。你只管把 Python 对象返回出去序列化和响应头的设置都是框架自动处理的。2.3 理解 FastAPI 的自动文档和类型校验这俩是最大卖点写好上面的代码后在终端里运行启动命令uvicorn app.main:app --reload --host 0.0.0.0 --port 8000这里把启动命令拆开讲。app.main:app表示“从app包里的main模块导入名为app的 FastAPI 实例”。--reload是开发模式下的热重载开关文件一保存服务器会自动重启并加载最新代码不用手动重启。--host 0.0.0.0表示监听所有网络接口这样同一局域网内的其他设备也能访问到你的服务如果只在本地调试也可以改成127.0.0.1。--port 8000是端口号如果 8000 被占用换成任意空闲端口即可。启动成功后浏览器访问http://127.0.0.1:8000/docs你会看到一个自动生成的 Swagger UI 页面里面清清楚楚地列着刚才定义的两个接口还可以直接在网页上测试接口并查看返回值。这个能力是 FastAPI 最吸引人的地方——你不需要额外写一行配置代码文档就自动生成了。FastAPI 的类型校验优势则需要你在路由函数中声明参数类型提现。举个例子from fastapi import FastAPI app FastAPI() app.get(/items/{item_id}) def read_item(item_id: int): return {item_id: item_id, name: fItem {item_id}}这里item_id: int声明了路径参数必须是整数。如果你访问/items/abcFastAPI 不会把请求转发给这个函数而是直接返回一个带详细错误信息的 422 响应告诉你参数类型不对。这种“写清楚类型就自动完成校验”的体验是 Flask 完全不具备的。3. 在 VSCode 中编写代码与调试把编辑器和框架的能力都用起来3.1 配置 Python 解释器为什么 Pylance 一开始没反应很多人装完 Python 插件后打开 VSCode发现代码补全和类型提示并没有生效原因通常是解释器没选对。VSCode 默认会尝试自动选择系统中能找到的 Python 解释器但如果你在项目里创建了虚拟环境VSCode 经常不会自动识别而是错误地选择了全局 Python 解释器导致 Pylance 分析和实际代码不匹配。正确的操作是按CtrlShiftPmacOS 是CmdShiftP打开命令面板输入 “Python: Select Interpreter”回车后 VSCode 会列出所有可用的解释器。选择刚才创建的项目虚拟环境路径通常类似.venv\Scripts\python.exeWindows 下或.venv/bin/pythonmacOS/Linux 下。选完之后VSCode 底部状态栏会显示解释器路径同时 Pylance 会开始索引项目代码补全和类型提示立刻生效。有一个细节值得注意如果你选择虚拟环境里的解释器之后再在 VSCode 的集成终端里新建终端VSCode 会自动激活虚拟环境命令行会带上(.venv)前缀。这意味着你不必在终端里手动执行激活命令直接运行uvicorn app.main:app --reload就能使用虚拟环境里安装的包。如果你新建终端后没有自动激活虚拟环境多半是python.terminal.activateEnvironment这个设置被关了去设置里把它勾上就行。3.2 用 VSCode 调试器替代 printlaunch.json 的配置方法我见过太多人调试 FastAPI 接口的方式是print()加注释这在接口逻辑简单的时候还能凑合一旦涉及数据库查询、外部调用就完全不够用了。VSCode 自带调试器完全支持 FastAPI 的断点调试配置好之后在行号左侧点击加红点请求进来时程序会停下你可以看到当前所有变量的值也可以一步步执行。新建.vscode/launch.json填入下面的配置{ version: 0.2.0, configurations: [ { name: FastAPI Debug, type: python, request: launch, module: uvicorn, args: [app.main:app, --reload, --port, 8000], jinja: true, justMyCode: true } ] }这里几个字段说明一下。module: uvicorn表示通过 Python 模块方式启动 uvicorn等价于在终端里执行python -m uvicorn。args里传的参数和命令行里传的完全一致。justMyCode: true表示只调试你自己写的代码不进入第三方库的内部实现避免调试时跳进一堆依赖包的代码里迷路。配置完成后按F5启动调试VSCode 会启动 uvicorn 并在调试模式下挂载此时访问接口命中断点后程序停在断点处左侧变量面板会展示请求对象、路径参数、上下文等所有信息。这个调试体验对标的是 PyCharm 专业版的断点调试但 VSCode 更轻量几乎没有启动延迟。3.3 值得装的小插件REST Client 的用法除了 Python 和 Pylance 两个核心插件我还是推荐一个非常实用的 REST Client 插件。这个插件让你在.http文件里直接写 HTTP 请求不用打开 Postman 或浏览器就能测试接口。新建一个test.http文件在 VSCode 里写入GET http://127.0.0.1:8000/health文件上方会出现一个“Send Request”按钮点击即可发送请求响应内容会展示在旁边窗口里。相比在浏览器里访问接口它的优势在于一是请求历史以文件形式保留可以提交到 Git 仓库团队同事能看到接口测试用例二是它支持环境变量可以定义多个环境一键切换。另外一个很多人容易忽略的设置是files.autoSave。在 VSCode 设置里把它设为afterDelay默认延迟 1000ms文件修改后自动保存。配合--reload热重载每次改完代码VSCode 自动保存、uvicorn 自动重启整个流程顺畅得让人上瘾。4. 常见问题与排查实录这些坑我基本都踩过4.1 uvicorn 日志丢失问题不是 bug 但特别容易误判热词里出现的“uvicorn fastapi 日志丢失问题”确实是个高发问题。现象是项目在--reload模式下跑起来代码里用logging库输出的 INFO 级日志和控制台自带的信息都不见了但服务本身还能正常工作。这个问题我第一次遇到时以为是代码 bug排查了很久。后来发现根因在 uvicorn 的日志配置和--reload机制上。--reload模式下uvicorn 会启动一个 reloader 进程来监控文件变化实际的 worker 进程由 reloader 管理。当你用logging.basicConfig()配置日志时这个配置只在当前进程生效reloader 进程如果重新加载了模块日志配置就丢失了。最稳妥的解决方式是不要在代码里依赖logging.basicConfig()而是用 uvicorn 自带的日志配置或者自己定义一个简单的日志函数import logging from fastapi import FastAPI logging.basicConfig(levellogging.INFO) logger logging.getLogger(uvicorn) app FastAPI() app.get(/) def read_root(): logger.info(Request received) return {message: Hello}如果你确实需要全面的日志配置更可靠的办法是用dictConfig配置一个完整的日志字典并把--reload模式下的日志输出重定向到一个统一的地方。但实际开发里我建议开发阶段就别在这种问题上较真先保证接口调通生产部署时再上正式的日志方案。4.2 端口被占用Windows 下最常用的解决方案启动 uvicorn 时提示[Errno 98] Address already in use或[WinError 10048]说明你指定的端口已经被其他进程占用了。最常见的原因是上一个 uvicorn 进程没有真正退出或者有别的服务占用了 8000 端口。Windows 下最快的排查方式netstat -ano | findstr :8000这条命令会列出所有监听 8000 端口的进程和对应的 PID。看到 PID 之后用下面的命令强制结束它taskkill /PID 进程号 /FmacOS 或 Linux 下用lsof -i :8000 kill -9 PID如果发现端口被其他无关进程占用不想动它更简单的方法是换个端口启动把启动命令里的--port换成 8001、8002 等空闲端口。4.3 修改代码后不生效先检查你是不是把服务停在了错误的状态“改了代码接口返回还是旧逻辑”——这类问题我收到过很多次求助。排除浏览器缓存因素后最常见的原因是启动命令里漏了--reload参数。没有热重载的情况下uvicorn 只有手动重启才会加载最新代码。另一个隐蔽原因是你在 VSCode 的调试面板里启动的调试会话和终端里启动的 uvicorn 是两个独立的进程。如果你同时用调试模式跑了一个服务、又在终端里跑了一个服务修改代码后其中一个会重启另一个不会请求打到老进程上表现就是“改了不生效”。解决方案是关掉冗余的服务进程只留一个在跑。还有一个和热重载相关的坑在多文件项目的场景下如果你只是在routers目录下的users.py里改了代码但main.py里 import 的是app.routers.users模块uvicorn 监视文件变化的范围默认是当前工作目录下的所有.py文件这种情况一般没问题。但如果你的项目里设置了--reload-dir参数指定了监视目录而目录范围漏了某个子目录就会导致改文件不触发重启。所以开发阶段干脆别加--reload-dir让它监视整个项目根目录即可。4.4 跨域问题前端联调时最常见的 405/403如果你是在做前后端分离的开发前端用的是 Vite 开发服务器或者 Vue/React 的 dev server访问后端接口时经常会遇到跨域报错。FastAPI 处理很简单安装fastapi.middleware.cors后注册 CORS 中间件允许你的前端域名访问from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins[http://localhost:5173], allow_methods[*], allow_credentialsTrue, allow_headers[*], )allow_origins里的http://localhost:5173是 Vite 开发服务器的默认地址换成你前端实际运行地址即可。开发阶段图省事可以直接填*但上线前一定要收紧只允许自己的前端域名访问否则任何网站都能向你的接口发起请求安全风险不小。5. 进阶案例FastAPI 调用本地大模型 Ollama5.1 为什么要把 FastAPI 和 Ollama 结合起来热词里“fastapi 调用 ollama”出现了好几次其实这个需求很自然本地运行一个大模型比如通过 Ollama 加载 Llama 3 或 Qwen 系列做推理很费资源你不会希望每次调用都去写一堆底层的客户端代码这时候用 FastAPI 包一层 HTTP 接口就变得非常有价值。简单说Ollama 负责加载模型和跑推理FastAPI 负责对外暴露一个标准的 RESTful API前端或其他后端服务调用这个接口就能得到 AI 生成的回复完全不用关心底层模型是什么、参数怎么调。这不仅把 AI 能力“产品化”了也把模型切换的复杂度隔离在 FastAPI 这一层。5.2 实现一个简单的 Chat 接口先确认你已经安装了requests库pip install requests在 FastAPI 项目里新增一个接口用httpx或者requests调用 Ollama 的本地 APIimport httpx from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): prompt: str app.post(/chat) async def chat(req: ChatRequest): async with httpx.AsyncClient(timeout60) as client: response await client.post( http://localhost:11434/api/generate, json{model: qwen2.5:7b, prompt: req.prompt, stream: False} ) data response.json() return {reply: data.get(response, )}这段代码里几个点说一下。使用httpx.AsyncClient是因为接口函数声明为async用异步客户端不会阻塞事件循环这样 FastAPI 可以同时处理其他请求。timeout60非常重要——本地大模型推理一个稍长的 prompt 可能需要几十秒如果默认超时时间太短请求就会被直接断开。stream: False表示一次性返回完整响应。如果追求更快的首字响应可以把stream设为True然后通过 StreamingResponse 把大模型一个字一个字吐出来的结果转发给客户端体验类似 ChatGPT 的打字机效果。这个作为扩展感兴趣的可以自己去试一下。5.3 异步和 gradio 的对比什么时候该用哪个前阵子很火的一个话题是“gradio 和 fastapi 谁更好”。实际上这两个东西定位完全不同Gradio 适合快速搭建带界面的 AI Demo几行代码就能跑出一个带输入框和输出框的网页FastAPI 则是生产级的后端框架适合把 AI 能力封装成标准 API 提供给业务系统调用。如果你只是在本地演示模型效果、给项目组看个 demoGradio 三分钟就能搞定如果你的目标是做产品让前端页面、小程序、或者另一个后端服务稳定地调用你的 AI 能力FastAPI 是正确的选择。两者甚至能同时用——把 Gradio 服务跑在一个端口上做演示把 FastAPI 服务跑在另一个端口上给前端调井水不犯河水。写在最后的几个实践心得整个流程走下来我最想说的是VSCode 配 FastAPI 这套组合上限很高但如果配置不到位下限也低得吓人。环境配好之后开发体验能到“写代码—保存—刷新—看到结果”的极速循环可一旦解释器选错、虚拟环境没激活、热重载没开就会陷入“改了不生效、报错看不懂”的泥潭。区别基本就在于前半小时的环境准备工作有没有做扎实。我自己现在的新项目起步流程基本固定了创建目录、用python -m venv建虚拟环境、装 fastapi、选解释器、写好launch.json、跑起来、打开http://localhost:8000/docs检查接口整套流程十分钟内完成。建议你也把这一步固化成一个自己的模板后面每次开新项目直接复制就行省下的时间真的不少。