HTTP 接口
在我们设计服务器或客户端的代码之前,让我们先来思考一下两者均会涉及的一点:双方通信的 HTTP 接口。
我们会使用 JSON 作为请求和响应正文的格式,就像第二十章中的文件服务器一样,我们尝试充分利用 HTTP 方法。所有接口均以/talks
路径为中心。不以/talks
开头的路径则用于提供静态文件服务,即用于实现客户端系统的 HTML 和 JavaScript 代码。
访问/talks
的GET
请求会返回如下所示的 JSON 文档。
[{"title": "Unituning",
"presenter": "Jamal",
"summary": "Modifying your cycle for extra style",
"comment": []}]
我们可以发送PUT
请求到类似于/talks/Unituning
之类的 URL 上来创建新对话,在第二个斜杠后的那部分是对话的名称。PUT
请求正文应当包含一个 JSON 对象,其中有一个presenter
属性和一个summary
属性。
因为对话标题可以包含空格和其他无法正常出现在 URL 中的字符,因此我们必须使用encodeURIComponent
函数来编码标题字符串,并构建 URL。
console.log("/talks/" + encodeURIComponent("How to Idle"));
// → /talks/How%20to%20Idle
下面这个请求用于创建关于“空转”的对话。
PUT /talks/How%20to%20Idle HTTP/1.1
Content-Type: application/json
Content-Length: 92
{"presenter": "Maureen",
"summary": "Standing still on a unicycle"}
我们也可以使用GET
请求通过这些 URL 获取对话的 JSON 数据,或使用DELETE
请求通过这些 URL 删除对话。
为了在对话中添加一条评论,可以向诸如/talks/Unituning/comments
的 URL 发送POST
请求,JSON 正文包含author
属性和message
属性。
POST /talks/Unituning/comments HTTP/1.1
Content-Type: application/json
Content-Length: 72
{"author": "Iman",
"message": "Will you talk about raising a cycle?"}
为了支持长轮询,如果没有新的信息可用,发送到/talks
的GET
请求可能会包含额外的标题,通知服务器延迟响应。 我们将使用通常用于管理缓存的一对协议头:ETag
和If-None-Match
。
服务器可能在响应中包含ETag
(“实体标签”)协议头。 它的值是标识资源当前版本的字符串。 当客户稍后再次请求该资源时,可以通过包含一个If-None-Match
头来进行条件请求,该头的值保存相同的字符串。 如果资源没有改变,服务器将响应状态码 304,这意味着“未修改”,告诉客户端它的缓存版本仍然是最新的。 当标签与服务器不匹配时,服务器正常响应。
我们需要这样的东西,通过它客户端可以告诉服务器它有哪个版本的对话列表,仅当列表发生变化时,服务器才会响应。 但服务器不是立即返回 304 响应,它应该停止响应,并且仅当有新东西的可用,或已经过去了给定的时间时才返回。 为了将长轮询请求与常规条件请求区分开来,我们给他们另一个标头Prefer: wait=90
,告诉服务器客户端最多等待 90 秒的响应。
服务器将保留版本号,每次对话更改时更新,并将其用作ETag
值。 客户端可以在对话变更时通知此类要求:
GET /talks HTTP/1.1
If-None-Match: "4"
Prefer: wait=90
(time passes)
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "5"
Content-Length: 295
[....]
这里描述的协议并没有任何访问控制。每个人都可以评论、修改对话或删除对话。因为因特网中充满了流氓,因此将这类没有进一步保护的系统放在网络上最后可能并不是很好。