现代网站越来越多的使用前后端分离架构,先用前端 MVC 框架快速堆砌出 SPA,再用 API 获取动态数据也已经成为日常的开发内容;而用来连接前后端的 API,其重要性也自然言而喻。做为一个前端码农,认识后端的 API 设计方式也是很重要的,今天让我们针对 API 设计来一探究竟。
毕竟是网站的前后端,其中间的通讯终究还是要依赖 HTTP 这个无状态的协议;在 HTTP 规范中定义了一系列的请求方法,其中较常用的如下:
GET
同为获取资源,但只取回 Header在规范中也提到,不同的方法指的是对同一件事做不同的操作,并通过语意化的方法,让不同的操作得到预期的结果。
大家对 GET
、POST
都不陌生,这是 HTML 的<form action="...">
所支持的两个方法;GET
是使用最频繁的,无论是获取得页面还是数据,一般都会用 GET
,而 POST
则常用在新增资源上,但由于 HTML <form action="...">
不支持其他方法,在传统网站中可能会用 POST
处里除了获取数据之外的所有事情。
PUT
和 PATCH
通常都用在更新资源上,两者的差异是 PUT
的行为是取代整个资源,而 PATCH
则是更新部分资源;把两者对应到日常生活中的话,就好比在餐厅吃饭,整桌菜重新点是 PUT
,另外加菜是 PATCH
。
DELETE
通常用在删除资源;HEAD
与 GET
类似,但只取回 Header,通常用在测试资源是否存在上;OPTION
是询问这个资源应该要怎样获取,通常用在发送 CORS 的预检(preflight)请求。
目前讲的都是在规范中提到且建议的一般用法,实际服务器的 API 怎么开发依然是看实现的人;但通过语意化的方法去设计 API,绝对可以让 API 对开发者更加友好。
前面所说只是规范,而且只涉及到了 HTTP 方法;有没有更完整的实现方法呢?
当然有,这就是大家耳熟能详的 RESTFul API;它由 Roy Fieldin 在 IEFT 开发HTTP 标准的六年间持续研究、验证的,并在 2000 年在他的博士论文 《Architectural Styles and the Design of Network-based Software Architectures》 中所提出,是一种网络程序的设计风格;所谓的 REST 是 表现层状态转换(Resource Representational State Transfer) 的缩写,简单说就是通过动词(HTTP 方法)、名词(URI/URL,代表目标资源)、内容型态(响应的内容,如 HTML、XML、JSON 等),让无状态的网络通信能通过 REST 的语意化设计,携带所有的状态进行通信,降低对网络的重复请求而造成的资源消耗。
例如假设有一个视频网站:myku.com
,它的的 API 有可能就会是这样:
[GET] http://myku.com/v1/videos/ -> 获取 video 列表 [POST] http://myku.com/v1/videos/-> 新增 video [GET] http://myku.com/v1/videos/MgphHyGgeQU -> 获取指定 ID 的 video [PUT] http://myku.com/v1/videos/MgphHyGgeQU -> 修改指定 ID 的 video [DELETE] http://myku.com/v1/videos/MgphHyGgeQU -> 刪除指定 ID 的 video
除了所使用的方法之外,也要注意代表资源的 URL 的编写方式,不是 HTTP 方法与实际动作相符合就算是 RESTful API !
同样的,RESTFul API 只是设计风格而不是 HTTP 的规范,很有可能在设计时基于 RESTful 的精神,但实际开发的结果却完全不是 RESTful 的风格;但不可否认的是通过 RESTful API 的设计风格,每个资源都会得到一个到对应的位置(URL),并能通过 HTTP 语意化的方法,对指定的资源做相对应的互动,整体资源管理会变得非常有语意化并且清晰,这确实是一个优秀的 API 设计方式。
在 HTTP 规范中提到要如何正确使用方法,如果我们没有按照规范实现,会造成一定的影响。
浏览器默认会对 GET
、HEAD
这两个方法做缓存,如果通过 POST
而不是 GET
获取资源的话,浏览器及中间的代理服务器一般都不会实现缓存机制,这时就必须由前后端开发自行通过其他方式设置缓存。
在规范中虽然也提到了POST
在 Header 合适的情况下也可以缓存,但由于实际上通常把POST
用在新增操作上,做缓存的话反而会造成不可预期的后果,大部分浏览器也都没有实现针对POST
的缓存机制。
当搜索引擎的爬虫在扫网站时,如果发现需要通过 POST
获取的资源,为了避免造成意外的行为或副作用,通常不会尝试爬取 POST
响应的结果。
虽然 RESTful API 的设计风格优点很多,但也有一些难以避免的缺点。
例如在查找存在依赖关系的嵌套数据时,很有可能必须要经过多次请求想要才能找到想要的结果;而随着项目架构逐渐扩张,同一页面的资料也会越来越复杂,可能需要多个来源的资料才能堆砌出页面,这时候 RESTful API 需要说明每个资源位置的特性,就会使 RESTful API 显得不太好用;也因为现在移动设备非常普及,一个后端服务器可能需要服务于 PC 版网页、手机 APP 等多设备的需求,需要的数据可能不一样,RESTful API 也就必须要实现出多个功能类似的接口,API 会逐渐变得庞大而且庞杂,相对难以管理。
有需求就会有解决的方法。这时 GraphQL 就应运而生了,这是由 Facebook 提出的开源语言标准,通过 Schema 定义资料,再依靠与 JSON 格式高度类似的查询语句取得查询的结果,它的主要特点是:
这些特性有效的解决了 RESTful API 在复杂架构下的问题,使 GraphQL 充满弹性、非常好用,社区也已经有了庞大的的生态系统支持,例如 Apollo GraphQL 可以与三大框架深度整合,再加上多查询合并的特性,让 GraphQL 与现代框架中组件的概念完美契合。
缺点大概就是必须要把所有复杂的数据拼接逻辑都实现在后端,对于习惯于 RESTful API 的开发者来说,需要付出不少学习成本。
值得注意的是 GraphQL 发出的全部都是 POST
请求,缓存机制必须仰赖开发者或是框架实现;例如在 Apollo Client 中,开发者必须按照应用场景,调整 fetchPolicy
的设置,避免快取造成的意外结果。
本文的标题是我一位朋友去面试某大厂后端时的一道面试题,由这个题目引申出 HTTP 方法及主流的 RESTful API 设计风格,并对 GraphQL 做了简短的介绍,希望以上内容能够帮到你。