Skip to main content

FastAPI

Intro​

HTTP 请求与响应​

一次 HTTP 通信可以分成请求和响应。请求由请求行、请求头、空行和请求体组成;响应由状态行、响应头、空行和响应体组成。

请求的组成​

部分作用HTTP 示例DeepSeek 请求中的例子
请求行指定 HTTP 方法、路径和协议版本GET /api/health HTTP/2POST /chat/completions HTTP/2
请求头描述请求及客户端信息Content-Type: application/jsonContent-Type: application/json、Authorization: Bearer ${DEEPSEEK_API_KEY}
空行标记请求头结束—curl 在请求头后自动添加分隔空行
请求体携带要提交的数据;有些请求没有请求体JSON、表单数据等{"model":"deepseek-flash","messages":[...]}

响应的组成​

部分作用HTTP 示例DeepSeek 响应中的例子
状态行返回协议版本、状态码和状态描述HTTP/1.1 200 OKHTTP/2 200
响应头描述响应内容及服务器信息Content-Type: application/jsoncontent-type: application/json、server: openresty
空行标记响应头结束—响应头与响应体之间的分隔行
响应体返回实际内容JSON、HTML、文本等{"id":"...","choices":[...],"usage":{...}}

常见状态码类别:2xx 表示请求成功,4xx 表示请求有问题,5xx 表示服务端处理失败。

常见请求头​

请求头含义DeepSeek 请求中的例子
Host请求访问的主机api.deepseek.com
User-Agent发起请求的客户端或工具curl/7.81.0
Accept客户端可以接收的响应格式*/*
Accept-Language客户端偏好的语言未发送
Content-Type请求体的数据格式application/json
Content-Length请求体的长度299(原始日志中的值)
Authorization身份凭证,例如 Bearer TokenBearer ${DEEPSEEK_API_KEY}(实际值应保密)
Cookie随请求发送的 Cookie未发送

常见响应头​

响应头含义DeepSeek 响应中的例子
Content-Type响应体的数据格式application/json
Content-Length响应体的长度日志中未显示
Server服务端软件信息openresty
Date响应生成时间Sat, 26 Sep 2026 04:42:10 GMT
Cache-Control缓存策略日志中未显示
Set-Cookie服务端要求客户端保存的 Cookie日志中未显示
Location重定向目标地址日志中未显示
Access-Control-Allow-Origin允许跨域访问的来源日志中未显示;该响应还包含 vary: origin, access-control-request-method, access-control-request-headers

示例:用 curl 请求 DeepSeek API​

下面通过 curl 向 DeepSeek 的 Chat Completions 接口发送 JSON 请求。-v 会输出连接、TLS 和 HTTP 交互过程,便于观察请求是如何发出的。

先将 API Key 放进环境变量,避免把密钥直接写入命令或笔记:

export DEEPSEEK_API_KEY="你的 API Key"

发送请求:

curl -v https://api.deepseek.com/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \
-d '{
"model": "deepseek-flash",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"thinking": {"type": "enabled"},
"reasoning_effort": "high",
"stream": false
}'

请求参数​

字段含义
model指定要调用的模型
messages对话消息列表;system 提供指令,user 提供用户输入
thinking设置是否启用思考模式
reasoning_effort指定推理强度
stream是否以流式方式返回;false 表示等完整结果生成后再返回
Content-Type声明请求体是 JSON
Authorization使用 Bearer Token 传递 API Key

curl -v 输出怎么看​

标记示例含义
** Connected to ...curl 的过程信息,例如代理连接、TLS 握手、上传进度;这些行不是 HTTP 请求或响应正文
>> POST /chat/completions HTTP/2curl 发给服务器的请求行和请求头
-d 参数"model": "deepseek-flash" 等 JSON 字段这是发送的请求体;curl -v 通常不会逐行打印请求体
<< HTTP/2 200服务器返回的状态行;200 表示请求成功
<< content-type: application/json服务器返回的响应头
无前缀{"id":"...","choices":[...]}响应体,也就是模型返回的数据

原始输出中的 * 行通常按连接顺序描述过程,例如:

阶段常见输出说明
代理配置* Uses proxy env variable ...curl 检测到并使用代理环境变量
建立连接* Trying ...、* Connected to ...尝试连接代理或目标服务器,并确认连接建立
建立隧道* Establish HTTP proxy tunnel ...通过 HTTP 代理建立到 HTTPS 服务的隧道
TLS 握手* TLS handshake ...、* SSL connection using ...协商加密连接并验证服务器证书
选择协议* ALPN ...、* server accepted to use h2协商使用 HTTP/2 或 HTTP/1.1
发送请求* We are completely uploaded and fine请求体已经发送完成
接收响应< HTTP/2 200服务端返回成功状态
结束连接* Connection ... left intactcurl 完成请求并处理连接

创建自己的 API​

下面用同一个 /api/hello 接口对比 Python 标准库和 FastAPI 的写法。

使用 Python 标准库 http.server​

from http.server import BaseHTTPRequestHandler, HTTPServer
import json

hello = {
"A": "Hello",
"B": "World",
}


class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/api/hello":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()

body = json.dumps(hello, ensure_ascii=True)
self.wfile.write(body.encode("utf-8"))
else:
self.send_response(404)
self.end_headers()


print("backend starts: http://localhost:8080/api/hello")
HTTPServer(("", 8080), Handler).serve_forever()

这里需要自己判断请求路径、设置状态码和响应头、把数据序列化成 JSON,再将响应内容编码并写入连接。


Python 常见的后端 Web 框架有:

框架简介
Flask轻量、灵活的 Web 框架
Django功能完整的 Web 框架
FastAPI基于类型注解构建 API,支持自动生成接口文档

FastAPI 官方文档:https://fastapi.tiangolo.com/

使用 FastAPI​

from fastapi import FastAPI

app = FastAPI()

hello = {
"A": "Hello",
"B": "World",
}


@app.get("/api/hello")
def get_hello():
return hello

用 Uvicorn 启动应用:

uvicorn main:app --host 127.0.0.1 --port 8080 --reload

main:app 表示从 main.py 中加载名为 app 的 FastAPI 实例。--reload 会在开发时监视代码文件,文件变化后自动重启服务。

FastAPI 自动完成了什么?​

工作标准库写法FastAPI 写法
匹配请求方法和路径在 do_GET 中手动检查 self.path根据 @app.get("/api/hello") 匹配
生成响应状态调用 send_response(200)默认返回 200 OK,也可以通过参数指定状态码
设置响应头手动调用 send_header返回字典时自动生成 JSON 响应头,如 Content-Type: application/json
把 Python 数据转为响应内容手动 json.dumps、编码并写入 wfile将返回的字典转换为 JSON 响应
处理错误路径手动返回 404没有匹配的路由时自动返回 404 Not Found
生成接口文档自己编写和维护根据路由和类型信息生成 OpenAPI 文档,可通过 /docs 查看交互式文档

FastAPI 还可以根据函数参数的类型和声明解析、校验查询参数、路径参数和请求体;校验失败时会生成相应的错误响应。当前 get_hello() 没有参数,因此这个例子没有展示参数校验。

FastAPI 负责应用层的路由、数据处理和响应构造;Uvicorn 负责运行 ASGI 应用、监听端口并处理 HTTP 与 ASGI 之间的通信。

@app.get("/api/hello") 装饰器的作用​

@app.get("/api/hello") 会在应用创建时把 get_hello 函数注册为路由处理函数,并声明它处理 GET /api/hello 请求。收到匹配请求时,FastAPI 调用 get_hello(),再把返回值转换成 HTTP 响应。

可以把它理解成下面这种显式注册的简写:

def get_hello():
return hello


app.add_api_route("/api/hello", get_hello, methods=["GET"])

装饰器不会在定义函数时立刻调用 get_hello();它是在应用启动、导入模块时登记路由,等请求到来后才调用处理函数。类似地,@app.post(...)、@app.put(...) 和 @app.delete(...) 分别用于注册其他 HTTP 方法的路由。

POST:接收 JSON 请求体​

POST 常用于向服务端提交数据。请求 JSON 会放在 HTTP 请求体中;测试时需要发送 Content-Type: application/json,告诉服务端请求体格式是 JSON。

用 dict 接收请求体​

from fastapi import FastAPI

app = FastAPI()

@app.post("/api/post_hello")
def post_hello(req: dict):
return {
"text": req["text"],
"id": 10086,
}

req: dict 中的类型注解告诉 FastAPI:req 是从请求体解析出的 JSON 对象。FastAPI 会读取 JSON 并将其转成 Python 字典,因此可以用 req["text"] 访问字段。普通 dict 不会声明具体有哪些字段,也不会像 Pydantic 模型那样对字段类型做细致校验。

测试请求:

curl -X POST "http://127.0.0.1:8000/api/post_hello" \
-H "Content-Type: application/json" \
-d '{"text":"Hello", "other":"none"}'

注意 JSON 字符串中的键和值要用英文双引号,字段之间用英文逗号。

用 Pydantic 声明请求模型​

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class AnalyzeRequest(BaseModel):
text: str
other: str

@app.post("/api/post_hello")
def post_hello(req: AnalyzeRequest):
return {
"text": req.text,
"id": 10086,
}

Pydantic 模型明确声明请求体需要 text 和 other 两个字符串字段。FastAPI 会根据模型解析并校验请求体;模型也会用于生成 OpenAPI 接口文档,因此 /docs 会展示请求字段和类型。字段缺失或类型不符合要求时,FastAPI 会返回校验错误。

读取原始 Request​

如果需要访问请求方法、完整 URL、请求头、查询参数或原始请求体,可以直接接收 Request:

用 Request 时,FastAPI 仍负责路由、接收 HTTP 请求和发送响应,但请求体由你手动读取和解析

from fastapi import FastAPI, Request

app = FastAPI()


@app.post("/api/post_hello")
async def post_hello(req: Request):
raw_body = await req.body()
body = await req.json()

return {
"method": req.method,
"url": str(req.url),
"headers": dict(req.headers),
"query_params": dict(req.query_params),
"raw_body": raw_body.decode("utf-8"),
"body": body,
"id": 10086,
}

Request 是 Starlette 提供的请求对象。读取 body 的接口是异步的,所以处理函数使用 async def,并通过 await 读取。与 Pydantic 模型不同,直接使用 Request 时,FastAPI 不会根据请求体自动生成字段 schema 或执行模型校验;需要自己解析和检查数据。

用前面的 curl 命令发送 {"text":"Hello", "other":"cc"},响应结构类似:

{
"method": "POST",
"url": "http://127.0.0.1:8000/api/post_hello",
"headers": {
"host": "127.0.0.1:8000",
"user-agent": "curl/7.81.0",
"accept": "*/*",
"content-type": "application/json",
"content-length": "30"
},
"query_params": {},
"raw_body": "{\"text\":\"Hello\", \"other\":\"cc\"}",
"body": {
"text": "Hello",
"other": "cc"
},
"id": 10086
}
写法适用场景自动校验与文档
req: dict快速接收结构简单、字段不固定的 JSON知道请求体是对象,但字段定义较宽泛
req: AnalyzeRequest请求字段明确,希望校验数据并生成清晰文档根据 Pydantic 模型校验,并把字段写入 OpenAPI 文档
req: Request需要访问原始请求、请求头或自定义解析请求体解析和校验由代码自行负责