最近疫情原因又在工位睡了几天,绷住,这就是深圳打工人的职业操守 🤣


记得陆奇刚回国就职百度,国内媒体争相报道,其中一篇问陆如何看待某某助手,陆回答的大意是将和助手打交道过程看作一个函数,输入 X,返回 Y。Y = f(X),这个看世界的视角很理工。

在 IT 世界,小到方法调用,大到服务调用,其实也遵循这个框架——将信息 X,传递给处理逻辑 f,从而获得信息 Y。风靡世界的 REST API (此处只讨论 JSON+HTTP 方式)就描述了一种将 X 传递给 f 并返回 Y 的方式。

REST 正式提出在 2000 年,Fielding 在博士论文 ( Architectural Styles and the Design of Network-based Software Architectures ) 中提出了 REST (Representational State Transfer) 风格架构。REST 风格架构基于 HTTP 协议 — HTTP 协议的目的就是为了共享资源(超文本文件),核心想法是尽量复用成熟的 HTTP 协议的能力。

在实际应用中,马丁福勒将对 REST 的应用情况划分为 4 个 level

  • level 0,使用 http 传输数据,如所有的 api 调用都封装在 POST 请求中,仅此而已;
  • level 1,有了资源的概念,在 URL 里标识资源;
  • level 2,使用 http 动词;
  • level 3,实现了 HATEOAS,落脚在 hypertext ,有点闭包的意思。

按此分类,很多实现方案可以直接归到 level 2 — 已经够用了,所以接下来介绍就止步到 level 2。

在 REST API 设计中,以资源为设计核心 — 资源通过 URL 来标识,围绕资源辅以一系列的动作 — 动作直接复用 HTTP 中的动词,通过 URL+HTTP Action 的组合来操作资源。目前大部分的 Web 系统都是事务型的系统,日常行为就是对数据库进行 CRUD,这种特点正好匹配 REST API 的设计思想。

如何设计呢?

首先,需要明确使用 API 的用户,在博文 the future of api design 中区分了两类用户:

  • SSKD(Small Set of Known Developers),少量你所知道的开发人员,一般针对不开放 api;
  • LSUD(Large Set of Unknown Developers),大量你所不知道的开发人员,一般针对开放 api;

显然,内部系统的目标群体属于 SSKD。

其次,从系统中识别出资源(如数据库中的实体、领域层中的实体),并将资源映射为 URL(存在标识单个资源和资源列表两种情况),可以参考以下几条设计原则

  • 只出现名词;
  • 短小便于输入;
  • 易读;
  • 没有大小写混用;
  • 修改方便;
  • 不会暴露服务端架构;
  • 规则统一;
  • 多个单词间可以通过 “-” 来连接;
  • 查询时的过滤条件用 query 参数传递;
  • 分页可以使用 page_size/page 或 limit/offset 的常用组合;

接下来内容默认读者具有 HTTP 协议基础知识,所以只拣要点介绍。

HTTP 中常用的请求动作映射到 REST 中,如下:

  • GET,获取资源
  • POST,新增资源
  • PATCH,更新部分资源
  • PUT,更新已有资源
  • HEAD,获取资源元信息
  • DELETE,删除资源

HTTP 请求由 header 和 body 组成,除了资源标识尽量放在 URL 中,其他参数一般放在 body 中,且 body 多采用 json 格式(扁平或层级结构)来组织。

http 响应时会返回状态码,包括:

  • 1xx,消息
  • 2xx,成功
  • 3xx,资源重定向
  • 4xx,客户端错误
  • 5xx,服务器错误

api 调用结果一般要体现在状态码上,不能调用失败却返回 200(错误信息只反应在响应体中),因为 REST 的思想是尽量复用 HTTP 协议的表达能力,包括 HTTP 协议衍生出来的生态,所以最好是将错误直接反应在状态码上,避免环境的调谐问题。

如果 api 调用错误,除了状态码应异常,响应中也会返回错误信息,错误信息一般由错误码(4 位数字)、错误信息组成。为了方便排查,错误码一般会使用首位数字进行粗粒度的分类;为了普通用户能看到友好的错误提示,错误信息还可以再分为 devmessage、usermessage 两类,生产环境就展示 usermessage。

在目前分布式、微服务占据主流的情况下,失败时的排查比较麻烦,一般技术方案里会建议采用工具进行调用链跟踪,个人觉得也可以在失败响应时,由最近的服务器在响应头部中插入自身的标识,以方便快速定位到出错的位置。

随着时间往前推进,API 必然会发生调整,从 embrace change 的角度出发,必须要设置版本号,可以设置在 URL 中,也可以在 header 头或者 body 中体现版本,并且不能直接下线老版本的 api,必须在新老版本同时运行一段时间后才能下线老版本的 api 接口,给足周边系统切换的时间。

在 Web 应用中有一条通用设计原则:尽量减少网络请求。很多时候延迟的大头花在网络上。比如前端需要请求好几个 api 才能组装出完整的一个页面,这时候就可以考虑提供一个统一的 api 给前端,一次就拿到该页面需要的所有数据。这样减少了延迟,节省了带宽,改善了用户体验,对以盈利为目的的企业来说就意味着实打实的企业利润。从这里引申出可以在贴近前端的地方做一个针对显示的 api 编排层,编排层替前端去执行多次 api 请求,组装好数据然后一次性返回。

api 接口设计还有缓存、攻击、限速、鉴权的内容,咱们下回分解.


参考:

  • 《Web API 的设计与开发》
  • 《The Design of Web APIs》
  • MDN

分类: CODE

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注