10 分钟搞懂 HTTP 与 API

你用过的每个应用,说到底都是两台电脑在互相递条子。HTTP 就是条子的格式,它简单到可以直接肉眼读。这一页讲清四件事:请求与响应模型、状态码到底在告诉你什么(401 和 403 的区别,一次说清)、不带教条的 REST,还有 CORS 报错的真相。

🎙️ 发布并录制于: ·

01基本模型:两张条子,然后各走各的

整个 HTTP 就是一次交换:客户端发一个 request,也就是一张有格式的条子;服务端回一个 response,另一张条子。然后是大多数人忽略的那一步,服务端就把你忘了。HTTP 是无状态的。你体验过的每一种「已登录」,都是靠每张条子上重新贴一次身份证做出来的,这张身份证就是 cookie 或者 token。

## the request — literally this readable:
GET /users/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbG...

## the response:
HTTP/1.1 200 OK
Content-Type: application/json

{"id": 42, "name": "Ada"}
无状态为什么和你有关
正因为无状态,你的会话才会在操作到一半时突然过期;调 API 才必须每一次都带上 key,而不是只在第一次带;负载均衡才成立,任何一台服务器都能回答任何一张条子,因为没有一张条子依赖上一张。所以当鉴权「莫名其妙失效」时,是身份证没贴上去,不是服务器把你忘了。

02拆开一个 URL

URL 是一个地址,有四个真正干活的部分:协议、楼(host)、房间(path),还有门上贴的便签(query 参数)。学会读它,API 文档就不再吓人了。

https://api.example.com/users/42/orders?status=paid&limit=10
└─┬──┘  └──────┬──────┘└──────┬──────┘ └────────┬─────────┘
scheme        host           path         query params
(how)       (which server)  (which thing)  (options: key=value, &-separated)
这条规矩很多人是吃过亏才记住的
query 参数会被到处记下来:服务端日志、代理、浏览器历史、统计工具。所以密钥永远不放 URL 里,?api_key=sk_live_... 这种 key,你就当它已经泄露了。密钥要走请求头(第 07 节),请求头默认不会被写进日志。

03方法:信封上的那个动词

method 告诉服务端这是哪一类条子。五个就够用了:GET 读,POST 建,PUT 整体替换,PATCH 改一部分,DELETE 删。它们背后真正值得弄懂的概念是幂等性:同一个请求发两遍安全吗?

GET    /users/42          # read — never changes anything
POST   /users             # create a new one  ⚠ twice = two users
PUT    /users/42          # replace entirely — twice = same result
PATCH  /users/42          # change some fields
DELETE /users/42          # delete — twice = still deleted
幂等性不是纸上概念
「支付按钮别点两次」就是一个幂等性 bug。GET、PUT、DELETE 重试都安全,POST 不安全。这就是支付类 API 要求你带上 Idempotency-Key 请求头的原因:重试过去的请求只会产生一笔扣款,不是两笔。POST 发出去一半网络超时,「到底成没成」这个问题,就是这个词存在的全部理由。

04状态码:到底是谁错了

第一位数字就是全部剧情:2xx 成了,3xx 搬走了,4xx错了,5xx他们错了。就这一句话,能把你以后遇到的每个 API 报错分成两堆:「改我的请求」和「等一等或者去报障」。还有那对千古纠缠的兄弟,这里一次说清:

200 OK               # here you go
201 Created          # made it (answer to a good POST)
301 Moved            # new address, go there
400 Bad Request      # your note is malformed (bad JSON, missing field)
401 Unauthorized     # WHO ARE YOU? — no/invalid credentials
403 Forbidden        # I know who you are. No. — valid login, no rights
404 Not Found        # no such thing at this path
429 Too Many Requests # slow down (rate limit — wait and retry)
500 Internal Error   # server crashed. not your fault
503 Unavailable      # server overloaded/down. also not your fault
401 与 403 的区别,一次说清
401 = 门口的保安看不懂你的证件:没带、过期,或者格式不对,去查你的 Authorization 请求头。403 = 你的证件没问题,但名单上没你:去查权限和 scope,别再折腾 token 了。这两个名字本身是反的,Unauthorized 表达的其实是未认证,一个五十年前的命名事故,所有人都将就着用到今天。

05请求头与 body:信封和里面的信

header 是写在信封上的元信息:什么格式、什么语言、谁在问、拿的是什么凭证。body 是信封里那张信,对 API 来说基本都是 JSON。你会反复设置两个请求头,再反复读第三个:

Content-Type: application/json    # "the body I'm SENDING is JSON"
Authorization: Bearer <token>     # "here's my ID card"
Accept: application/json          # "please REPLY in JSON"

# the body (POST /users):
{ "name": "Ada", "country": "UK" }
这个坑你一定会踩
你 POST 了一份完全正确的 JSON,拿回来一个 400 Bad Request,或者一个字段全是 null 的怪结果。因为你忘了写 Content-Type: application/json,服务端把你的 JSON 当表单提交解析了。body 没错,是信封上的标签错了。一个请求头,解释了 Stack Overflow 上十年的提问。

06curl:在终端里跟任何 API 说话

curl 从命令行发 HTTP 请求。它的用处是把你自己的应用挪开去测 API,这也是最好用的一招排查:curl 通、代码不通,bug 在你的代码里;curl 也不通,问题在 API 那边。分而治之。

# read
curl https://api.example.com/users/42

# create (-X method, -H header, -d body)
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "Ada"}'

# see the STATUS CODE and headers too (the debugging view)
curl -i https://api.example.com/users/42

07鉴权:API key 与 bearer token

两种模式覆盖了大多数 API。API key 是给程序用的长期密码。bearer token 通常有效期很短,靠登录换来,用 Authorization: Bearer … 的形式出示。bearer 的字面意思是谁拿着它,谁就是它。这一个词既说明了安全模型,也就是那句安全警告。

# both ride in the Authorization header:
Authorization: Bearer eyJhbGciOi...     # token (often a JWT)
x-api-key: sk_live_4242...              # some APIs use a custom header

# keys live in environment variables, never in code:
curl -H "Authorization: Bearer $API_TOKEN" ...
三条铁律
不进 URL(到处都会被记下来,见第 02 节)。不进 git(爬虫几分钟就能扒到,事故处理流程看 Git 那一页)。不进前端代码(查看源码就等于泄露)。key 放环境变量和密钥管理服务里;一旦怀疑某个 key 可能泄露了,先轮换,再调查。

08REST,不谈教条

REST 是一套命名约定,不是一种技术:URL 里放东西(名词),方法说要对它做什么(动词)。百分之九十就这些。一个 API 算不算 RESTful,标准是你能不看文档猜出下一个 endpoint,可预测性就是它的全部意义。

# nouns in the URL, verbs in the method:
GET    /articles          # list articles
POST   /articles          # create one
GET    /articles/7        # read one
PATCH  /articles/7        # edit it
DELETE /articles/7        # remove it
GET    /articles/7/comments   # nested: its comments

✗ POST /getArticleById   ✗ GET /deleteUser?id=7  # verbs in URLs = smell
说句实话
你会遇到有人争论「什么才是真正的 REST」,扯 HATEOAS、扯 Roy Fielding 的博士论文。笑一笑,走开。实际工作里,「REST API」就是这一节展示的东西:可预测的名词 URL、标准动词、JSON。这套词汇足够你在任何真实代码库里干活,也足够应付面试题。

09CORS:那个报错不是它看起来的意思

有一天你的前端去调一个 API,控制台开始尖叫。这里有个几乎人人搞错的地方:这个报错不是服务端拒绝了你,是你自己的浏览器在执行一条安全规则:来自站点 A 的 JavaScript 不能读站点 B 的响应,除非 B 明确允许,也就是回一个 Access-Control-Allow-Origin 响应头。证据很简单,同一个请求用 curl 发就完全正常。

Access to fetch at 'https://api.other.com/data' from origin
'http://localhost:3000' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present...

# decode: api.other.com didn't say localhost:3000 may read it.
# the browser fetched the data — then withheld it from your JS.
谁才真的能修
只有服务端能修 CORS,方法是回那个允许头。前端再怎么写,都不可能正当地让它消失。你的选择是:这个 API 是你的,就为你前端的 origin 加上响应头;是别人的,就从你自己的后端去调,让前端调你的后端(后端不是浏览器,CORS 管不到它)。浏览器上那些「关闭 CORS」的插件只修好了你这台机器,然后对每一个真实用户都是坏的。

10速查表

这几张表能回答百分之九十的 API 问题。

# methods
GET read · POST create (⚠ not retry-safe) · PUT replace
PATCH edit · DELETE remove

# status: first digit = who messed up
2xx ✓ · 3xx moved · 4xx you · 5xx them
401 who are you? → fix the token
403 you specifically? no. → fix the permissions
429 slow down → wait and retry

# the POST that "mysteriously" fails
→ did you send Content-Type: application/json ?

# debugging order
curl -i first. code second.
curl works + code fails → your bug
curl fails too        → their bug (or your token)

# CORS error in console
→ browser rule, server must allow it. curl proves the API works.

# secrets
headers ✓ · env vars ✓ · URLs ✗ · git ✗ · frontend ✗

这就是能干活的核心。再花哨的东西,webhook、GraphQL、gRPC,都是同样两张条子的变体。等你想用代码去调 API,Python 加上这一页就是一套完整的工具。

Tell me what missed

A correction is more useful than a compliment. This goes straight to the person who writes SwiftGrasp.

Was this page useful?
0/1000

Please do not include passwords, private keys, or personal information.