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备注
浏览器 Fetchfetch(url, {method:"POST", ...})前端标配,受 CORS 限制
curl(终端)curl -X POST -H "Content-Type: application/json" -d '{"k":"v"}' url调试/脚本首选,-X 指定方法
openai SDKclient.chat.completions.create({...})内部就是 POST,封装了方法/头/body
axiosaxios.post(url, body, {headers})第三方库,自动 JSON、自带拦截器
Python requestsrequests.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.com

5. 典型工作流

前端调后端 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.mdcreate() 剥开就是 POST /v1/chat/completions
  • ../ai-agent-guide/api_relay.md — 中转站收到的正是这些 POST 请求
  • CORS — OPTIONS 预检的由来,跨域请求为什么被浏览器拦
  • WebSocket — 需要服务器主动推时,HTTP 请求-响应不够用,要升级成长连接