API 风格
你会记得你吃过多少片面包吗?其实,面包 (BREAD) 还有另一层含义,是 browse、read、edit、add、delete 的简写啦。
本章节没有太多的知识,主要想与你讨论一下,在 Milkio 中,编写 BREAD 的最佳风格是什么?顺便,和你分享一下流行的 REST 的历史。
REST 与历史
REST 是一种很流行的 BREAD 风格,它通常是这样子的:
名称 | HTTP Method | URL | HTTP Body |
---|---|---|---|
browse | GET | /user/ | - |
read | GET | /user/1 | - |
edit | PUT | /user/1 | { "name": "alice" } |
add | POST | /user/ | { "name": "alice" } |
delete | DELETE | /user/1 | - |
REST 是一个伟大的风格,它兴起于千禧年,并在接下来的十年中盛行。
在那个时代,前后端还不像现在这样泾渭分明,只有 RD,没有 FE 和 PE。服务器通过拼接 HTML 来动态地改变内容,没有 JavaScript 或者只是用来为网页添加一些交互效果,直到 Gmail 利用 JavaScript 为世界带来了一些小小的震撼,人们才意识到 JavaScript 的潜力,最终演变成一场席卷整个世界的飓风。
网页内容逐渐不再由服务器预先渲染,而是由 XMLHttpRequest 动态地获取,服务器越来越多地变为了单纯地向前端提供 XML 或 JSON 格式的数据,不再是混合了数据与界面的 HTML。
终于,尘归尘,土归土。用户所接触到的一切,都是由运行在浏览器中的 JavaScript 创造的,服务器只保留了提供数据和保证安全性等关键部分。
REST 现在的问题
如今,对于客户端而言,有着自己的路由,通常是使用 history 或者 location.hash 实现的。REST 之所以不再适合,原因之一就在于客户端与服务端间路由的彻底分离。
浏览器默认发送 GET 请求。GET 方法用于展示页面,其他方法用于执行动作。这也是为什么 REST 将 browse 和 read 都使用 GET 方法实现的原因。
后来,由于历史惯性,大家仍然保持着 REST 的编程习惯,即便,现在提供的已经不再是页面,而是数据。
然而,GET 请求存在一个重要问题:它没有请求体。对于 REST 而言,如果想向服务器传递参数,只能将其作为目录的一部分(例如 /user/1
)或 URL 参数(例如 /user/?id=1
)。
这个限制使我们无法向服务器传递复杂参数,并且在过程中会丢失类型信息,无法区分数字和布尔值(因为所有参数都从 URL 中截取),也不支持数组和对象。
REST 的优势之一是为用户提供漂亮且符合直觉的 URL。但随着前端拥有自己的路由功能后,用户已经看不到我们服务器的URL了。此外,在使用 REST 时编写API需要考虑多少种参数传递方式呢?
方式 | 示例 | 缺点 |
---|---|---|
URL 目录 | /user/1 | 只能传递单值、没有类型、不支持数组和对象 |
URL Params | /user/?id=1 | 没有类型、不支持数组和对象 |
Request Header | X-Foo-Bar | 没有类型、不支持数组和对象 |
Request Body | {"name": "alice"} | 无 |
Request Method | POST | 用于表达执行动作的类型,为 GET、POST、PUT、DELETE 其中之一 |
此外,REST 在许多情况下也会限制我们的 API 表达能力。当我们想要实现与用户相关的 API,包括注册、登录和 BREAD 操作时,直觉上的 URL 应该是这样的:
在 REST 中,如果我们严格遵循规范,就会面临选择登录和注册的 HTTP 方法的困扰。如果我们将注册尚且可以视为添加,使用 POST 方法,那么,登录又该怎么办呢?而且,如果我的管理后台,需要拥有一个添加用户的方法,如何区分添加和注册?
该使用怎样的风格
是时候,采用一种在 REST 基础上进行简化的新风格了!
对于这种新风格,首先,应该所有的请求都使用 POST 方法,这样就可以全部使用 Body 来传递参数数据了。
并且,为了能够实现类型安全,Body 中使用 JSON 这种拥有类型的数据格式,抛弃掉 form-data/urlencoded 等老旧的数据格式。
既然没有了方法之分,那么,我们可以用 URL 中的目录代表资源,而文件名则用来描述动作,来完成取代方法的职责。
至于参数部分,所有的参数,全部放在 Request Body 内,对于 Milkio 而言,以 JSON 形式通过 Body 发送的数据,会被序列化为你代码中的 params
,并享有类型安全。一个实际的请求如下:
而在 Milkio 中,你可以轻易地接收它。
BREAD 模板
当你学会使用 Milkio,并决定要使用的其他技术栈后,你可能会觉得每次重复编写 BREAD 代码都很繁琐。通过 Milkio 的模板 功能,你可以将这个工程变得很轻松。