聊聊 60 分的 Web API

字数 3304阅读 571

扯 dan

应用程序接口(英语:Application Programming Interface,简称:API),又称为应用编程接口,就是软件系统不同组成部分衔接的约定。由于近年来软件的规模日益庞大,常常需要把复杂的系统划分成小的组成部分,编程接口的设计十分重要。程序设计的实践中,编程接口的设计首先要使软件系统的职责得到合理划分。良好的接口设计可以降低系统各部分的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合程度,从而提高系统的维护性和扩展性。

以上是 WiKi 对 API 的描述,日常开发中笔者通常以 API(接口) 来狭义地指代 Web API(Web Service)。当然这种现象可能只存在于移动互联网行业里(甚至 Only me ?有其它称呼可以留言哦),毕竟人家做硬件也会提供 API 。反正下文中笔者以 API 来指代 Web API ,少按 5 次键盘 🕵。
聊聊 60 分的 Web API ,这个 Topic 其实也是笔者 YY 出来的,实在是墨水有限。为何只是“聊聊 60 分的”,笔者认怂,自认只了解 60 分的水平(实际 30?及格分总要给吧 😳),希望大 🐂🐂 能不吝指导一二。接下来笔者会先简单描述下了解的三种 API 类型,然后再写下对于 60 分 API 应该具有的“素质”。


正文

API 风格

笔者了解到的 API 协议/架构风格主要有三种 —— RPC & SOAP & REST。 其中对于 RPC 和 SOAP 协议了解不够深入,下文着重描述 REST。

RPC

RPC,Remote Procedure Call 是一种计算机编程协议,该协议允许一套计算机调用另一台计算机上的子程序。RPC 定义了结构化的 Request 和 Response,以 GET 、POST 作为主要请求方法。请求数据格式可以是 XML,也支持更轻量的 JSON。

// XML RPC Request
<?xml version = "1.0"?>
<methodCall>
    <methodName>getUserName</methodName>
    <params>
        <param>
            <value>
                <id>40</id>
            </value>
        </param>
    </params>
</methodCall>
  
// XML RPC Response
<?xml version = "1.0"?>
<methodResponse>
    <params>
        <param>
            <value>
                <string>KentonYu</string>
            </value>
        </param>
    </params>
</methodResponse>
SOAP

SOAP,Simple Object Access Protocol 是 Web Service 交换数据的一种协议,基于 XML-RPC 进行优化修改。它不仅支持 HTTP/HTTPS,还支持 SMTP 协议。它比 XML-RPC 更强大,但是数据格式只支持 XML。


SOAP XML 请求格式
SOAP XML 请求格式
REST

REST,Representational State Transfer(表述性状态转移)是一种架构风格,而不是协议。它是 Roy Thomas Fielding 博士于 2000 年在他的博士论文 "Architectural Styles and the Design of Network-based Software Architectures" 中提出来的,但是在 5 年后才开始受到关注。REST 通常基于 HTTP,URI 以及 JSON、XML 等协议和标准实现。当 REST 风格应用在 API 上时,我们称之为 RESTful API。
REST 并没有具体的规范,它只是一种风格。何为风格?提供一系列约束,但是不强制性要求必须满足这些约束。
REST 架构风格的形成,主要遵循了以下几点:

  1. 关注点分离;
  2. 可见性、可靠性和可伸缩性;
  3. 网络性能、效率;
  4. 统一接口;
    总得来说,REST 是为了让 Web 服务更加高效、可靠、通用。

相对于 RPC(面向动作)、SOAP(消息机制),REST 是面向资源。资源,可以是服务器数据库的一张表,一张图片,或者说更加抽象的东西(当然这个东西要便于客户端开发理解)。
它主要是从以下三个方面来标识资源:

  1. 直观简短描述性的 URI;
  2. 传输的资源格式(JSON,XML,HTML.etc.);
  3. 对资源的操作,一般通过 HTTP 的一系列请求方法来表示;


    HTTP 请求方法在 RESTful API 中的典型应用
    HTTP 请求方法在 RESTful API 中的典型应用

那么 RESTful API 具体长什么样子?接下来我们就来看看 Github 的 API 设计,它是遵循 REST 风格的。


Github API 获取用户邮箱地址
Github API 获取用户邮箱地址

这就是一个简单的遵循 REST 的 API,使用 HTTP 的 GET 方法指出是读取服务器资源,通过 URI “/user/emails” 来定位到具体的资源。REST 是面向资源的,因此在设计 URI 时是不会出现类似 “getEmails” 这样的动词。响应结果通过状态码(Status Code)来标识,具体响应数据通过 JSON 格式传输,当然也可以使用 XML 格式,甚至 HTML。

Github API 添加用户邮箱地址
Github API 添加用户邮箱地址

增加用户邮箱地址,采用 POST 请求方法,URI 仍旧是 “/user/emails”,因为这两个 API 操作的资源都是 Emails,因此 URI 是一致的。它们通过使用的请求方法(GET、POST、PUT、DELETE .etc.)来区分不同的动作。

小结

以上简略地描述了下 RPC、SOAP、REST 的概念,它们三者的目的是一致的 —— 作为客户端和服务端沟通桥梁。但是它们之间也是各有特点,比如 RPC 面向动作,SOAP 以消息作为通讯机制,REST 则面向资源。在实际开发的技术选型中,了解这三种的优劣势,在其中权衡,选择最高效、最适合的技术方案即可。当然,技术思想都是在不断发展进步的,后起之秀 REST 综合指数一定是比 RPC/SOAP 更具优势,它更适合一个新产品、新公司的技术选型,因为 REST 紧密贴合 HTTP,扩展性和兼容性更强,并且通过描述性的 URI 调用,客户端开发效率更高。

60 分 API 的素质

API 的一致性

一致性,大致可以分为两类:风格一致性和数据一致性。
风格一致性可能对于优化 API 的性能来说起不到实质性的作用,但是它的确可以提高团队的开发效率。如果一套 API 保持一致的风格,并且正确使用自描述性的命名,将会很大程度上提高 API 使用者的开发效率以及降低后续的维护成本。为了保持风格一致性团队必须有一份 API 规范文档来约束,这是必要的。
数据一致性主要提现在 API 的幂等性上,HTTP 的请求方法中 GET 、PUT、DELETE 应该保证是幂等的 —— 相同条件下,多次请求不会产生副作用。

安全性

API 的安全性在整个开发过程中毋庸置疑是最重要的环节之一,一套没有安全性可言的 API 对于用户或老板来说都是不负责任的表现。因为 API 是数据和用户连接的桥梁,API 的安全性直接关系到用户信息的安全性以及整个应用的安全性。因此一个 60 分的 API 应该具有一定的自我保护能力

  1. 数据合法性验证
    记得学封装的概念时看到的一句话,大致意思是暴露的 API 都必须保证数据的合法性,因为你永远不知道调用者会传什么数据给你。对于 Web API 来讲,它更不可能知道调用者会是谁(iOS、Andriod、模拟请求工具、黑客.etc.),会传什么数据给你,因此对于每一个 API 都应该进行数据合法性验证。一次请求包括 URI QueryString、 Request Header、Body ,API 应该一步步的判定这些数据的合法性,它们之间应该是“与”的关系。
    最基础的数据验证应该做到过滤无效(或不合常理)数据,比如 API 入参有一个 age(interger) 字段,当某个请求传入 -1 时,就应该将拒绝这次不合法的请求,因为 age 不可能存在负数。
    做到以上这一步只能说保护了数据库不会被写入不符合预期的数据,但是也要考虑到一些数据符合我们的约定,但是它本身具有攻击性,比如脚本注入,SQL 注入等。这些常见的攻击手段都应该在这一环节屏蔽掉。

  2. 数据完整性验证
    请求数据需要经过网络的传输才能到达服务器端,在这过程中,API 需要确保请求数据是否完整(相对于客户端发送请求时的数据)。比如从云盘下载文件时,如果你的文件在传输过程中丢了一些包,那么这个文件将不能打开,并且这次下载是无效的。因此 API 要确保每一次请求的数据都是完整的,这可以通过请求的参数生成一个 MD5 验证字符串,在服务端验证数据完整性。
    传输过程中请求数据可能会丢包或者被修改,那么数据库中的数据也可能会被其它请求修改,因此修改数据库数据时要确保数据的完整性,当客户端使用和数据库不一致的数据时,应该拒绝这次请求。这可以通过使用 ETag(某个资源的一个唯一的版本号,也可以用来实现 Cache )来防止错误更新,使用 "If-Match" 头来提供客户端的 ETag,判断和服务器资源当前版本是否一致(当然高并发时会存在 A 请求正在准备写入数据库,而 B 刚进入 ETag 验证,并且恰好通过)。

  3. API 访问控制
    对于大多数应用,它的 API 一定是不想公开被其它人使用的,尤其是竞争对手。一套合格的 API 应该具有一个合理的访问控制。目前较为普遍的是使用 HMAC 验证,通过一个密钥对请求信息进行算法处理,生成一个摘要信息,然后服务端通过同样的密钥进行处理,比对摘要信息是否一致来确定是否是合法请求。通过 HMAC 可以防止不合法的请求,并且保证数据没有受到“中间人”攻击。
    除了 HMAC,还有 OAuth、HTTP BASIC Auth 等访问控制手段,当然你也可以自定义一些逻辑算法,比如可以将请求参数按首字母排序,加上请求时间戳(请求的实时性,防止一个 Request 在不同时间里重复请求)利用 DES、3DES、AES 等对称加密算法进行加密,生成一个请求密钥,服务端通过相同的逻辑算法来比对请求密钥,从而实现访问控制。
    访问控制可以很大程度上避免 DDOS 攻击,防止服务器资源被“榨干”。

API 日志收集

API 应该收集每个请求的到达时间,处理时间,请求数据,响应数据,请求客户端信息等。日志的收集,有助于解决一些 bug,对于一些 API 的性能优化,以及运营人员的分析等。

丰富的 API 文档

一份丰富的 API 文档可以让客户端开发变的更加容易。把容易留给别人,那你就是佛祖,否则你就是人渣。文档中应该包含对所有 API 的一些通用描述,每个 API 都应该对应有 URI,请求头、参数,响应数据等,当然最好附带一个接口调试工具。这部分应该是服务端开发最头疼的事情了,每个 API 都要对应维护一份文档,但是目前有很多工具可以通过维护代码里的注释生成文档,从而减少开发的工作量,比如 swagger 、apidoc .etc.。

其它

除此之外,API 还应该支持例如 API 版本管理、事件处理回调等,具有良好的扩展性、后期可维护性等。针对安全性较高的 API,可以考虑使用 HTTPS ,在你的 API 外面再套一层壳。


结束语

笔者只是通过查阅相关的文献、博客等,总结了以上的内容。如有补充,欢迎留言。希望后续开发中,能将这些一一应用,码一套 60 分的 API。


Reference

推荐阅读更多精彩内容