HTTP 请求方法 与 Fetch API(怎么真正发出一个请求)
1. 定义
HTTP 方法(Method / 动词):GET / POST / PUT / DELETE / PATCH / HEAD / OPTIONS,写在请求行里,告诉服务器”这次请求想干什么”——读、建、改、删。协议层含义见 http.md 第 4 节,本文偏实际怎么用。
Fetch API:浏览器/Node 内置的客户端函数,用来真正发出一个 HTTP 请求并拿回响应。它是”怎么把 POST/GET 这些动词发出去”的工具。
一句话总结:方法 (Method) 是”你想干啥”的动词,Fetch 是”把这句话说出去”的嘴巴。 你用 Fetch 指定一个方法(默认 GET),它帮你组好报文、发到服务器、再把响应交还给你。
类比:方法像点餐动作(“取/点/退”),Fetch 像服务员——你告诉他”对后厨用 POST 说’来一份’“,他跑腿送达并带回菜品。
2. 七大方法速查(动词语义)
| 方法 | 语义 | 幂等? | 数据放哪 | 典型场景 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | URL 查询参数 | 读页面、查数据 |
| POST | 提交 / 新建 | 否 | 请求主体 (body) | 提交表单、调 API(如聊天) |
| PUT | 整体替换/创建 | 是 | body | 全量更新一个资源 |
| PATCH | 局部更新 | 否 | body | 只改几个字段 |
| DELETE | 删除 | 是 | 通常无 body | 删除资源 |
| HEAD | 只要头不要主体 | 是 | — | 探测资源是否存在/是否变更 |
| OPTIONS | 询问支持哪些方法 | 是 | — | CORS 预检 |
幂等:发一次和发 N 次效果一样(GET/PUT/DELETE 是,POST/PATCH 否)。POST 不幂等 → 提交两次可能下两单,所以浏览器刷新 POST 页面会警告”是否重新提交”。 深度理论(报文格式、状态码、无状态)见
http.md。
3. Fetch API:浏览器里怎么发出去
基本形状
// 默认 GET
const res = await fetch("https://api.example.com/v1/models");
const data = await res.json(); // 解析 JSON 主体
// 指定方法 + 头 + body(POST 调一个 OpenAI 兼容端点,呼应 ai-agent-guide)
const res = await fetch("https://中转站/v1/chat/completions", {
method: "POST", // ← 动词在这里
headers: {
"Content-Type": "application/json", // 告诉服务器 body 是 JSON
"Authorization": "Bearer 你的key",
},
body: JSON.stringify({ // body 必须是字符串
model: "gpt-4o-mini",
messages: [{ role: "user", content: "你好" }],
stream: false,
}),
});
const data = await res.json();
console.log(data.choices[0].message.content);关键点
- 默认 GET:不写
method就是 GET。 method大写:用"POST"/"PUT"等,大小写不敏感但约定大写。body必须字符串化:Fetch 不替你JSON.stringify,对象直接丢会报错。- 读响应:先
await res.json()(或res.text())拿主体;res.status是状态码,res.ok表示 2xx。 - 错误处理:Fetch 只有网络失败才 reject,4xx/5xx 也算”成功收到响应”,要自己
if (!res.ok) throw。
// 健壮写法:4xx/5xx 也要显式抛错
async function callAPI() {
const res = await fetch(url, { method: "POST", /* ... */ });
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
return res.json();
}流式(SSE):逐字接收
// OpenAI 兼容的流式输出:body 里 stream:true,用 ReadableStream 逐块读
const res = await fetch(url, { method: "POST", body: JSON.stringify({ ..., stream: true }) });
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
process.stdout.write(decoder.decode(value)); // 逐块吐字,像打字机
}4. 同义对照:不同客户端怎么发同一个 POST
| 客户端 | 怎么发 POST | 备注 |
|---|---|---|
| 浏览器 Fetch | fetch(url, {method:"POST", ...}) | 前端标配,受 CORS 限制 |
| curl(终端) | curl -X POST -H "Content-Type: application/json" -d '{"k":"v"}' url | 调试/脚本首选,-X 指定方法 |
| openai SDK | client.chat.completions.create({...}) | 内部就是 POST,封装了方法/头/body |
| axios | axios.post(url, body, {headers}) | 第三方库,自动 JSON、自带拦截器 |
| Python requests | requests.post(url, json=body, headers=...) | 自动 JSON 序列化 |
同一件事多种写法,本质都组出同一份 POST 报文。你之前学的
openai_compatible_sdk.md里的client.chat.completions.create(...),剥掉糖衣就是POST /v1/chat/completions;中转站api_relay.md收到的也正是这个请求。
curl 速查
# GET(默认)
curl https://api.example.com/v1/models
# POST + JSON body
curl -X POST "https://中转站/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 你的key" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"你好"}]}'
# 看原始报文(排查用)
curl -v https://example.com5. 典型工作流
前端调后端 API(POST 提交)
浏览器 JS ── fetch(POST /api/order, body) ──▶ 服务器(建订单)
浏览器 ◀── 201 Created + 订单数据 ──────────── 服务器
调一个 AI 模型(呼应近期笔记)
你的代码(fetch / openai SDK)
│ POST /v1/chat/completions
▼
中转站(兼容端点,见 api_relay.md)
│ 翻译 + 转发
▼
真实模型供应商
你换
base_url就能换供应商,正是因为请求形状(POST + 这套字段)是固定的——见openai_compatible_sdk.md。
接手前端项目:找请求发哪
1. 搜 fetch( / axios. / requests. 定位所有出站请求
2. 看 method 和 url:是直连还是走了中转站域名
3. 看 body/headers:鉴权是不是在 Authorization 头(别放 URL,会被日志泄露)
6. 常见误区
❌ “Fetch / axios 是另一种 HTTP 方法”
正确:它们是发请求的客户端工具,方法(GET/POST…)是它们发出的动作。Fetch 默认 GET,靠 method 参数切换成 POST。
❌ “POST 比 GET 更安全”
正确:只是数据在 body 不在 URL(不被日志/历史记录),但明文传输一样能被截获。要安全靠 HTTPS(见 http.md 第 8 节),不是靠换方法。
❌ “Fetch 返回 404 会抛异常”
正确:Fetch 只在网络层失败才 reject;404/500 也是”成功拿到响应”,await fetch() 照样 resolve,需自己查 res.ok / res.status。
❌ “Fetch 的 body 直接传对象就行”
正确:body 必须是字符串(或 FormData/Blob),JSON 对象要先 JSON.stringify,否则请求会出错或发出 [object Object]。
❌ “GET 能带 body” 正确:协议上 GET 不该有 body,多数服务器/网关会忽略;要传数据用查询参数(且有长度限制、会被记录)。
❌ “CORS 是服务器不让我请求”
正确:CORS 是浏览器出于安全拦下跨域响应,服务器返回 Access-Control-Allow-Origin 头即放行;OPTIONS 预检就是为此;它不阻止 curl/服务端发起的请求。
7. 延伸阅读 / 关联概念
http.md— HTTP 协议全貌:报文格式、状态码、无状态、HTTPS、版本演进(本文方法理论的完整版)server.md— 请求的”另一端”:服务器怎么收、怎么响应、会话保持socket.md— HTTP 底层靠 socket 收发字节tcp-udp.md— 传输层底座,理解队头阻塞与 HTTP/3../ai-agent-guide/openai_compatible_sdk.md—create()剥开就是 POST /v1/chat/completions../ai-agent-guide/api_relay.md— 中转站收到的正是这些 POST 请求- CORS — OPTIONS 预检的由来,跨域请求为什么被浏览器拦
- WebSocket — 需要服务器主动推时,HTTP 请求-响应不够用,要升级成长连接