AI应用-HTTP基础
一、HTTP 是什么(30秒建立直觉)
HTTP 就是浏览器和服务器之间说话的规矩。每次你访问网页、调用 API,都是在发送 HTTP 请求、接收 HTTP 响应。
你(客户端) 服务器
│ │
│ ── 请求 (Request) ──────────> │
│ GET /users/1 │
│ Host: api.example.com │
│ │
│ <── 响应 (Response) ───────── │
│ 200 OK │
│ {"id": 1, "name": "张三"} │
│ │
一个完整的 HTTP 交互永远包含两部分:请求(你发出去的)和响应(服务器还回来的)。
二、HTTP 方法(动词)
每个请求都有一个方法,告诉服务器"我想干什么":
|
方法 |
语义 |
FastAPI 写法 |
典型场景 |
|
|
读取资源 |
|
查用户、查列表、查详情 |
|
|
创建资源 |
|
注册、发消息、上传文件 |
|
|
完整替换 |
|
整体更新用户信息 |
|
|
部分更新 |
|
只改用户名 |
|
|
删除资源 |
|
删文章、删会话 |
GET vs POST 的本质区别:
GET /search?q=RAG&limit=10 ← 参数放在 URL 里,可以书签收藏
POST /chat ← 参数放在请求体里,可以发大量数据
{"message": "你好", "session_id": "abc"}
GET 请求不应该修改数据(刷新页面重发 GET 没问题),POST/PUT/PATCH/DELETE 会改变数据(刷新页面浏览器会提示"是否重新提交")。
三、请求的结构
一个完整的 HTTP 请求由三部分组成:
POST /chat/stream HTTP/1.1 ← 请求行:方法 + 路径 + 协议版本
Host: api.example.com ← 请求头开始
Content-Type: application/json ← 告诉服务器请求体的格式
Authorization: Bearer eyJhbGci... ← 认证信息
Accept: text/event-stream ← 告诉服务器期望的响应格式
← 空行,分隔头和体
{ ← 请求体(Body)
"message": "公司年假政策是什么?",
"session_id": "abc-123"
}
最重要的请求头:
Content-Type: application/json # 请求体是 JSON
Content-Type: multipart/form-data # 上传文件时用
Authorization: Bearer <token> # JWT 认证,几乎所有 API 都用
Accept: application/json # 告诉服务器我期望 JSON 响应
四、响应的结构
HTTP/1.1 200 OK ← 响应行:协议版本 + 状态码 + 描述
Content-Type: application/json ← 响应头
X-Request-ID: req-abc-123 ← 自定义头(调试用)
← 空行
{ ← 响应体
"answer": "公司员工每年享有15天带薪年假...",
"sources": [{"filename": "hr_policy.pdf", "page": 3}]
}
五、状态码(必须记住的)
状态码告诉你请求的结果。分五类,记住第一个数字的含义就行:
2xx — 成功
200 OK 最常见的成功,GET 请求返回数据
201 Created POST 创建成功(FastAPI 用 status_code=201 标注)
204 No Content DELETE 成功,但没有响应体返回
3xx — 重定向
301 Moved Permanently 永久跳转(换域名时用)
302 Found 临时跳转(登录后跳回原页面)
4xx — 客户端错误(你发的请求有问题)
400 Bad Request 请求格式错误,参数不对
401 Unauthorized 没有认证(没带 token 或 token 过期)
403 Forbidden 认证了但没有权限(用户A访问用户B的数据)
404 Not Found 资源不存在
405 Method Not Allowed 用了错误的 HTTP 方法(对只读接口发 POST)
409 Conflict 冲突(注册时邮箱已存在)
422 Unprocessable Entity FastAPI 默认用这个表示请求体验证失败
429 Too Many Requests 限流(请求太频繁)
5xx — 服务器错误(服务器出了问题)
500 Internal Server Error 服务器代码崩了
502 Bad Gateway 上游服务挂了(OpenAI API 超时)
503 Service Unavailable 服务器过载或维护中
504 Gateway Timeout 上游服务响应太慢
在 FastAPI 里使用状态码:
python
from fastapi import HTTPException
from fastapi import status # 用常量比硬编码数字更可读
# 返回 201
@app.post("/documents", status_code=status.HTTP_201_CREATED)
async def upload_doc(file: UploadFile):
doc = await save_document(file)
return doc
# 手动抛出错误
@app.get("/users/{user_id}")
async def get_user(user_id: int, db=Depends(get_db)):
user = await db.fetchrow("SELECT * FROM users WHERE id=$1", user_id)
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"用户 {user_id} 不存在"
)
return user
# 401 vs 403 的正确用法
# 没带 token → 401 Unauthorized("你是谁?我不知道")
# 带了 token 但访问别人数据 → 403 Forbidden("我知道你是谁,但你没权限")
六、JSON(数据格式)
JSON 是 HTTP API 里最主流的数据格式,Python 字典和 JSON 几乎一一对应:
python
import json
# Python 对象 → JSON 字符串(序列化)
data = {
"id": 1,
"name": "张三",
"tags": ["AI", "后端"],
"active": True,
"score": 9.5,
"address": None # Python None → JSON null
}
json_str = json.dumps(data, ensure_ascii=False)
# '{"id": 1, "name": "张三", "tags": ["AI", "后端"], "active": true, ...}'
# JSON 字符串 → Python 对象(反序列化)
back = json.loads(json_str)
print(back["name"]) # "张三"
print(type(back["active"])) # <class 'bool'>
```
**Python 和 JSON 的类型对应**:
```
Python JSON
dict → object { }
list → array [ ]
str → string ""
int/float → number
True/False → true/false
None → null
FastAPI 自动处理 JSON,你不需要手动 json.loads() 和 json.dumps():
python
from pydantic import BaseModel
class ChatRequest(BaseModel):
message: str
session_id: str | None = None
# FastAPI 自动把请求体 JSON 解析成 ChatRequest 对象
@app.post("/chat")
async def chat(body: ChatRequest):
print(body.message) # 直接用,不需要 json.loads()
return {"answer": "..."} # 直接返回字典,FastAPI 自动序列化成 JSON
```
---
## 七、URL 的结构
```
https://api.example.com:8000/users/123?include=profile&format=json#section1
│ │ │ │ │ │ │ │
协议 域名/IP 端口 路径 路径参数 查询参数 片段(前端用)
三种传参位置,在 FastAPI 里对应三种写法:
python
# 1. 路径参数(Path Parameter)— 资源的唯一标识
@app.get("/users/{user_id}/sessions/{session_id}")
async def get_session(user_id: int, session_id: str):
# GET /users/42/sessions/abc-123
...
# 2. 查询参数(Query Parameter)— 过滤、分页、排序
@app.get("/documents")
async def list_documents(
page: int = 1,
size: int = 20,
status: str | None = None,
q: str | None = None,
):
# GET /documents?page=2&size=10&status=ready&q=政策
...
# 3. 请求体(Request Body)— POST/PUT/PATCH 的数据
@app.post("/chat")
async def chat(body: ChatRequest):
# POST /chat Body: {"message": "你好", "session_id": "abc"}
...
```
---
## 八、用工具调试 HTTP(实践比看文档重要)
推荐 [Bruno](https://www.usebruno.com/)(免费桌面客户端)或 [Hoppscotch](https://hoppscotch.io/)(免费在线)。
启动上一节做的 FastAPI 项目后,直接在工具里发请求感受一下:
```
# 创建物品
POST http://localhost:8000/items/
Content-Type: application/json
{
"name": "机械键盘",
"price": 399.0,
"in_stock": true
}
# 期望响应:
# 201 Created
# {"id": 1, "name": "机械键盘", "price": 399.0, ...}
```
FastAPI 自带的 `/docs` 页面(Swagger UI)也可以直接在浏览器里测试,不需要额外安装工具。
---
## 九、SSE(流式输出的底层)
AI 应用里流式输出用的是 **Server-Sent Events**,本质是 HTTP 连接保持不断开,服务器持续推数据:
```
← 普通 HTTP:一问一答,等全部生成完才返回
客户端: POST /chat
服务器: (等5秒){"answer": "这是完整的回答..."}
← SSE:边生成边推,用户立刻看到内容
客户端: POST /chat/stream
服务器: data: 这\n\n
服务器: data: 是\n\n
服务器: data: 流\n\n
服务器: data: 式\n\n
服务器: data: 输\n\n
服务器: data: 出\n\n
服务器: data: [DONE]\n\n
SSE 格式固定:每条消息以 data: 开头,以 \n\n 结尾。这就是为什么 FastAPI 流式接口要这样写:
python
@app.post("/chat/stream")
async def chat_stream(body: ChatRequest):
async def generate():
async for chunk in stream_from_llm(body.message):
yield f"data: {chunk}\n\n" # SSE 格式
yield "data: [DONE]\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
十、推荐资料
|
资料 |
链接 |
说明 |
|
MDN HTTP 指南 |
最权威的 Web 基础文档 |
|
|
HTTP 状态码速查 |
每个状态码的含义和使用场景 |
|
|
RESTful API 设计 |
REST 规范,API 设计必读 |
|
|
FastAPI 文档 |
把 HTTP 概念和代码直接对应起来 |
学 HTTP 最快的方式是:把你做的 FastAPI 项目跑起来,用 Bruno 或 /docs 页面发各种请求,故意发错的参数看 4xx,故意访问不存在的 ID 看 404。在实际请求和响应里看状态码,比背定义快 10 倍
更多推荐


所有评论(0)