正文
用户打开日历软件,可以点击会议查看详情。程序通常不需要模仿这些点击,而是按照服务提供的接口,发送查询请求,再处理返回的数据。这里会经常遇到 HTTP 和 REST API。
HTTP 是一种应用层通信协议,用来约定客户端与服务端怎样交换请求和响应。API 则是程序可以使用的接口;REST 是一种设计分布式系统的架构风格。 这三个名称描述的角度不同,先沿着一次会议查询看看它们怎样配合。
假设我们要查询编号为 m42 的会议。应用文档可能约定一个地址:
https://calendar.example/meetings/m42
这个地址完全用于教学,没有对应本文实际访问的会议服务。https 表示采用受 TLS 保护的 HTTP 通信;calendar.example 是示例服务的主机名;后面的路径标识我们准备访问的目标。
程序向服务发出请求时,属于客户端;接收请求并提供会议资料的一方,属于服务端。这里描述的是这次交互中的角色,不是说一台电脑永远只能承担其中一个角色。
为了看清请求内容,可以用 HTTP/1.1 的可读形式展示它的主要部分:
GET /meetings/m42 HTTP/1.1
Host: calendar.example
Accept: application/json
第一行的 GET 是请求方法,表达这次准备怎样访问目标;路径指出目标会议;HTTP/1.1 表示这里采用的报文版本。Host 指出目标主机,Accept 则说明客户端希望收到 JSON 形式的内容。
Host 和 Accept 这样的项目叫“首部字段”,也常简称“请求头”。它们提供与请求有关的信息。需要提交通知正文时,请求还可能带有内容,也就是日常所说的请求体。不同 HTTP 版本的报文表示方式有所不同,上面的文本不能直接当作所有版本的实际传输格式。HTTP 请求与响应的组成
服务收到查询后,需要找到对应记录,并检查当前访问者是否有权读取。假设这些条件满足,它可以返回如下示意响应:
HTTP/1.1 200 OK
Content-Type: application/json
{"meeting_id":"m42","title":"项目例会"}
这里同样只展示主要内容,不是一次真实接口调用的运行结果。200 是状态码,帮助客户端了解请求处理情况;Content-Type 说明返回内容采用什么媒体类型;空行后面是会议数据。JSON 的具体写法会在下一篇展开。
请求中的 Accept 表达客户端希望接受的类型,响应中的 Content-Type 描述这次实际提供的类型。二者不能互相替代,程序也不应只看到一个成功状态,就把后面的任意内容都当成 JSON。
查询会议以后,如果用户希望保存一份通知草稿,应用还可能调用另一个接口。文档可能约定:向 /drafts 提交 POST 请求,在请求体中提供会议编号和草稿正文,由服务创建草稿记录。
这说明请求方法会影响操作含义。GET 用来取得目标资源的表示;POST 让目标资源按自身约定处理提交的内容,创建记录是常见用途之一。PUT 通常表达创建或替换指定目标的状态,DELETE 通常用来请求删除目标资源。具体接口支持哪些方法,仍然需要查看文档。HTTP 方法的标准含义
DELETE 这个名称不保证底层存储立即彻底擦除,POST 也不只用于“新增一行数据”。方法提供通用语义,会议服务还需要说明它在自己业务中的具体行为。
请求完成以后,程序需要结合状态码与响应内容处理结果。例如,404 可能表示没有找到目标记录,也可能是服务不愿披露其存在;403 表示服务理解请求却拒绝执行;500 表示服务端遇到意外情况。
这些信息帮助定位问题,但不必然覆盖整个用户任务。即使读取会议返回 200,也只能说明这一步查询的处理情况,后面的草稿可能尚未生成。202 则表示请求已被接受处理,并不说明处理已经完成。
通信也可能在收到响应之前中断。此时客户端不知道结果,不等于服务端一定没有执行操作。如果创建草稿已经成功,只是响应丢失,再次提交就可能创建第二份。
为了理解什么时候可以重试,需要认识两个容易混淆的属性。
HTTP 中的 safe,通常译为“安全方法”,说的是方法定义的语义基本只读,客户端没有通过它请求改变目标状态。它不代表身份安全,也不保证实现完全没有日志等副作用。GET 属于这类方法。
idempotent 通常译为“幂等”,关注多个相同请求的预期效果是否与执行一次相同。例如,重复请求把同一目标设置成相同内容,与每次新建一份草稿不同。PUT、DELETE 和安全方法按协议具有幂等语义,但响应未必相同,实现也可能记录每次调用。安全方法与幂等方法的协议含义
所以,不能只看名字就断定某个实现绝不会改变数据,或者任何失败都能放心重试。接入实际服务时,还要看接口是否遵守这些语义,以及是否提供结果查询、去重或其他恢复机制。
认识 HTTP 以后,再看 REST 就容易一些了。
把一场会议看作资源,为它提供稳定标识,再交换这场会议的结构化表示,是理解资源式接口的一个入口。返回的 JSON 可以表示会议资料,但它不是服务端内存或数据库本身;服务也可以用其他格式表达资源。
REST 还对系统交互施加一组约束,包括客户端与服务端职责分离、无状态交互、缓存、统一接口和分层等。严格的统一接口也包含利用表示中的链接推动后续交互,而不只是把地址命名为名词。这些原则来自 Fielding 对 REST 的原始论述。REST 架构风格与约束
其中,“无状态”最容易被误读。它要求一次请求包含服务理解这次请求所需的信息,而不是让服务猜测客户端上一次进行到了哪里。这并不禁止服务保存会议、用户和通知草稿等业务数据。
因此,使用 HTTP 不自动等于符合 REST,返回 JSON 也不是 REST 的判定条件。实际文档对“REST API”的使用可能比较宽泛,开发者仍然要阅读它对地址、方法、输入、输出和访问条件的具体约定。
对会议助手来说,API 文档就像一份程序之间的使用说明。它需要告诉我们怎样筛选会议、日期使用什么格式、结果是否分页、错误怎样表示,以及访问需要什么凭证。不能仅凭一个看起来熟悉的地址猜出全部行为。
例如,会议列表接口如果分几页返回结果,只读取第一页,就不能断言日历里没有目标会议。程序需要按接口提供的分页约定继续取得资料,或清楚说明目前只检查了哪一部分。日期字段也应结合接口规定的时区理解,不能把没有核对的本地时间直接写进通知。
HTTPS 保护传输过程,也不能代替服务端的访问授权。程序持有某个账户的凭证,更不等于当前任务允许它读取所有资料或修改全部会议。协议通信、账户能力与用户授权仍然需要分别落实。
在大模型 Agent 中,模型可以提出“查询会议 m42”的工具请求;应用再把它转换成符合接口约定的 HTTP 请求,取得响应并检查结果,随后将相关内容提供给模型。模型生成了一段请求说明,并不会让远程服务自动执行操作。
现在,我们已经看到了程序怎样向外取得会议数据。下一篇将顺着响应体里的那对花括号继续,认识 JSON,以及数据怎样从程序内部变成可以传输与保存的内容。
当前学习编号:02.04 HTTP 与 REST API。
下一学习编号:02.05 JSON 与序列化。后续还有其他学习编号。
关联分支:接口接入对应 16.12 API、数据库、代码与浏览器工具接入、16.05 工具调用请求与实际执行的区别;操作语义对应 16.09 只读操作与有副作用操作、16.10 工具幂等与重复执行。
访问与恢复对应 22.05 身份认证与访问授权、24.04 超时与截止时间、24.06 重试、指数退避与抖动、24.07 幂等键与去重。这些支系会在各自编号中单独展开。