跳到主要内容
Cowers://
全部文章
服务端与存储

FastAPI 请求全链路原理

从浏览器地址栏敲下回车,到页面显示结果,中间到底发生了什么。 这一篇讲原理;要查语法直接用 FastAPI 学习地图 的症状速查表;要系统学看讲义系列(从 01 HTTP 与 API 接口基础 开始)。

FastAPI 请求全链路原理

从浏览器地址栏敲下回车,到页面显示结果,中间到底发生了什么。 这一篇讲原理;要查语法直接用 FastAPI 学习地图 的症状速查表;要系统学看讲义系列(从 01 HTTP 与 API 接口基础 开始)。


一句话总览

浏览器访问:

http://127.0.0.1:8000/users/1

完整过程大概是:

浏览器
  ↓
解析 URL
  ↓
DNS 查询 IP
  ↓
建立 TCP 连接
  ↓
发送 HTTP 请求
  ↓
服务器网卡收到数据
  ↓
操作系统交给端口对应的进程
  ↓
Uvicorn 接收请求
  ↓
转换成 ASGI 请求
  ↓
FastAPI 路由分发
  ↓
执行你的 Python 函数
  ↓
生成 HTTP 响应
  ↓
通过 TCP 发回浏览器
  ↓
浏览器解析响应并展示

一、浏览器输入网址后发生了什么

假设你访问:

http://127.0.0.1:8000/items/100?name=book

这个 URL 可以拆成:

部分 含义
http 协议
127.0.0.1 服务器地址
8000 端口
/items/100 路径
?name=book 查询参数

二、浏览器先确定“请求发给谁”

1. 如果是域名,需要 DNS 解析

比如:

https://www.example.com

浏览器不知道 www.example.com 是哪台机器,所以要查 DNS:

www.example.com → 93.184.216.34

DNS 的作用就是:

把域名翻译成 IP 地址。

如果是:

127.0.0.1
localhost

就不用真正去公网查 DNS。

127.0.0.1 表示:

本机

三、TCP 连接:浏览器和服务器建立通道

HTTP 请求不是直接“飞过去”的。

它通常跑在 TCP 连接上。

浏览器要先和服务器建立 TCP 连接:

浏览器: 我要连你
服务器: 可以连
浏览器: 好,开始通信

这就是常说的 TCP 三次握手。

简化版:

浏览器 → 服务器:SYN
服务器 → 浏览器:SYN + ACK
浏览器 → 服务器:ACK

建立完成后,双方之间就有一条可靠的数据通道。


四、端口是什么

IP 找到的是一台机器。

端口找到的是这台机器上的某个程序。

比如你的电脑上:

程序 端口
FastAPI/Uvicorn 8000
MySQL 3306
PostgreSQL 5432
Redis 6379
SSH 22

所以:

127.0.0.1:8000

意思是:

本机上的 8000 端口对应的程序

你启动 FastAPI 时:

uvicorn main:app --reload

Uvicorn 默认监听:

127.0.0.1:8000

也就是:

操作系统收到发往 8000 端口的数据后,交给 Uvicorn 进程。

五、HTTP 请求长什么样

浏览器最终发出去的不是 Python 对象,而是一段文本格式的数据。

例如:

GET /items/100?name=book HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Chrome
Accept: application/json

如果是 POST 请求,可能还会有请求体:

POST /users HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/json
 
{
  "name": "Tom",
  "age": 18
}

HTTP 请求主要分成:

请求行
请求头
空行
请求体

例如:

GET /items/100?name=book HTTP/1.1     ← 请求行
Host: 127.0.0.1:8000                 ← 请求头
User-Agent: Chrome                   ← 请求头
 
                                      ← 空行
请求体                                ← GET 一般没有请求体

六、服务器机器收到请求后发生什么

请求到达服务器后,先不是 FastAPI 直接处理。

先经过:

网卡
  ↓
操作系统内核
  ↓
TCP/IP 协议栈
  ↓
找到端口 8000
  ↓
交给监听 8000 的进程

也就是:

Uvicorn

七、Uvicorn 是干什么的

FastAPI 本身不是直接监听端口的程序。

FastAPI 是一个 Web 框架。

真正负责监听端口、接收 TCP 数据、解析 HTTP 的是:

Uvicorn

你可以理解成:

Uvicorn = 服务器门卫 + HTTP 接收员
FastAPI = 后台业务系统
你的函数 = 具体办事的人

启动命令:

uvicorn main:app --reload

含义是:

找到 main.py 里的 app 对象
用 Uvicorn 启动它
监听 HTTP 请求

八、main:app 是什么意思

假设你有:

# main.py
from fastapi import FastAPI
 
app = FastAPI()

命令:

uvicorn main:app --reload

意思是:

main      → main.py 文件
app       → main.py 里的 app 变量
--reload  → 代码改动后自动重启

所以 Uvicorn 会加载:

from main import app

九、ASGI 是什么

FastAPI 不是直接和 Uvicorn 乱连的。

它们之间有一个规范,叫:

ASGI

ASGI 可以理解成:

Python Web 服务器和 Python Web 框架之间的通信标准。

也就是规定:

Uvicorn 收到请求后,应该用什么格式交给 FastAPI。
FastAPI 处理完后,应该用什么格式把响应交还给 Uvicorn。

所以整体关系是:

浏览器
  ↓ HTTP
Uvicorn
  ↓ ASGI
FastAPI
  ↓
你的路由函数

十、FastAPI 内部怎么处理请求

假设代码是:

from fastapi import FastAPI
 
app = FastAPI()
 
@app.get("/items/{item_id}")
def get_item(item_id: int, name: str):
    return {
        "item_id": item_id,
        "name": name
    }

浏览器访问:

GET /items/100?name=book

FastAPI 会做这些事:

1. 接收 Uvicorn 传来的 ASGI 请求
2. 读取请求方法:GET
3. 读取请求路径:/items/100
4. 匹配路由:/items/{item_id}
5. 提取路径参数:item_id = "100"
6. 根据类型标注 int,把 "100" 转成 100
7. 读取查询参数:name = "book"
8. 调用你的函数:get_item(item_id=100, name="book")
9. 拿到返回值
10. 转成 JSON
11. 包装成 HTTP 响应
12. 交给 Uvicorn 发回浏览器

十一、路由分发细节

你写的:

@app.get("/items/{item_id}")
def get_item(item_id: int):
    return {"item_id": item_id}

相当于告诉 FastAPI:

如果收到 GET /items/某个值
就调用 get_item()

FastAPI 内部大概维护一张路由表:

请求方法 路径规则 函数
GET /items/{item_id} get_item
POST /users create_user
GET /users list_users

收到请求后:

GET /items/100

FastAPI 查表:

GET 匹配 GET
/items/100 匹配 /items/{item_id}
所以执行 get_item()

这就是路由分发。


十二、参数从哪里来

FastAPI 会根据函数参数自动判断参数来源。

1. 路径参数

@app.get("/items/{item_id}")
def get_item(item_id: int):
    return item_id

请求:

GET /items/100

来源:

item_id 来自 URL 路径

2. 查询参数

@app.get("/items")
def get_items(page: int = 1, size: int = 10):
    return {"page": page, "size": size}

请求:

GET /items?page=2&size=20

来源:

page=2
size=20

3. 请求体参数

from pydantic import BaseModel
 
class User(BaseModel):
    name: str
    age: int
 
@app.post("/users")
def create_user(user: User):
    return user

请求体:

{
  "name": "Tom",
  "age": 18
}

FastAPI 会把 JSON 转成:

User(name="Tom", age=18)

十三、FastAPI 为什么能自动校验类型

因为 FastAPI 读取了你的类型标注:

def get_item(item_id: int):

它知道:

item_id 应该是 int

如果请求:

GET /items/100

可以转成整数:

item_id = 100

如果请求:

GET /items/abc

不能转成整数,FastAPI 自动返回:

{
  "detail": [
    {
      "msg": "Input should be a valid integer"
    }
  ]
}

你的函数不会执行。

也就是说:

请求参数不合法
  ↓
FastAPI 直接拦截
  ↓
返回 422 错误

十四、你的函数执行完以后发生什么

比如:

@app.get("/items/{item_id}")
def get_item(item_id: int):
    return {
        "item_id": item_id,
        "name": "book"
    }

返回的是 Python 字典:

{
    "item_id": 100,
    "name": "book"
}

但是浏览器不能直接接收 Python 字典。

FastAPI 会把它转成 JSON:

{
  "item_id": 100,
  "name": "book"
}

然后包装成 HTTP 响应:

HTTP/1.1 200 OK
content-type: application/json
 
{
  "item_id": 100,
  "name": "book"
}

十五、HTTP 响应长什么样

HTTP 响应也分几部分:

状态行
响应头
空行
响应体

例如:

HTTP/1.1 200 OK
content-type: application/json
content-length: 29
 
{"item_id":100,"name":"book"}

状态码常见有:

状态码 含义
200 成功
201 创建成功
400 请求有问题
401 未登录
403 没权限
404 路由不存在
422 参数校验失败
500 服务器内部错误

十六、响应怎么回到浏览器

响应返回路线基本反过来:

你的函数
  ↓
FastAPI 转 JSON
  ↓
Uvicorn 转 HTTP 响应
  ↓
操作系统 TCP 发送
  ↓
网络传输
  ↓
浏览器接收
  ↓
浏览器解析响应

如果响应头是:

content-type: application/json

浏览器知道这是 JSON。

如果是:

content-type: text/html

浏览器就按 HTML 页面渲染。


十七、完整流程图

用户访问 URL
  ↓
浏览器解析 URL
  ↓
DNS:域名 → IP
  ↓
TCP 三次握手,建立连接
  ↓
浏览器构造 HTTP 请求
  ↓
请求通过网络到服务器
  ↓
服务器操作系统收到数据
  ↓
根据端口交给 Uvicorn
  ↓
Uvicorn 解析 HTTP
  ↓
Uvicorn 按 ASGI 标准交给 FastAPI
  ↓
FastAPI 查路由表
  ↓
提取路径参数 / 查询参数 / 请求体
  ↓
Pydantic 做类型转换和数据校验
  ↓
执行你的路径操作函数
  ↓
函数返回 Python 对象
  ↓
FastAPI 转成 JSON 响应
  ↓
Uvicorn 发回 HTTP 响应
  ↓
浏览器收到响应
  ↓
浏览器展示结果

十八、用一段 FastAPI 代码串起来

from fastapi import FastAPI
from pydantic import BaseModel
 
app = FastAPI()
 
class User(BaseModel):
    name: str
    age: int
 
@app.post("/users/{user_id}")
def update_user(user_id: int, user: User, token: str):
    return {
        "user_id": user_id,
        "name": user.name,
        "age": user.age,
        "token": token
    }

请求:

POST /users/10?token=abc123 HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/json
 
{
  "name": "Tom",
  "age": 18
}

FastAPI 判断:

参数 来源
user_id 路径 /users/10
token 查询参数 ?token=abc123
user JSON 请求体

最终调用:

update_user(
    user_id=10,
    token="abc123",
    user=User(name="Tom", age=18)
)

返回:

{
  "user_id": 10,
  "name": "Tom",
  "age": 18,
  "token": "abc123"
}

十九、再压缩成一句核心理解

FastAPI 的完整请求处理可以记成:

浏览器发 HTTP 请求
→ Uvicorn 接收
→ ASGI 转交
→ FastAPI 路由分发
→ 参数解析和校验
→ 执行你的函数
→ 返回 JSON
→ Uvicorn 发回浏览器

最关键的是分清三层:

层 负责什么
Uvicorn 监听端口,接收 HTTP
ASGI Uvicorn 和 FastAPI 之间的通信规范
FastAPI 路由分发、参数校验、执行函数、生成响应

相关:FastAPI 学习地图 · 01 HTTP 与 API 接口基础 · 从项目学 FastAPI 全景笔记