API
API, Application Programming Interface,应用编程接口,就是软件系统不同组成部分衔接的约定。一般来说,当我们今天引用API时,我们更具体地指的是通过超文本传输协议(HTTP)提供的Web API。对于这种特定情况,API指定消费者如何使用API公开的服务:可用的URI,每个URI可以使用哪些HTTP方法,它接受哪些查询字符串参数,哪些数据可以在请求正文,以及消费者可以期待的响应。
大多数新式 Web 应用程序都会公开 API,客户端可以使用这些 API 来与该应用程序交互。 设计良好的 Web API 应旨在支持:
- 平台独立性。 不管 API 的内部实现方式如何,任何客户端都应该能够调用该 API。 这就需要使用标准协议并创建一种机制,使客户端和 Web 服务能够就交换数据的格式达成一致。
服务演变。 Web API 应能在不影响客户端应用程序的情况下改进和添加功能。 随着 API 的发展,现有客户端应用程序应可继续运行而无需进行任何修改。 所有功能应该是可发现的,使客户端应用程序能够充分利用它。
API类型
Remote Procedure Call (RPC)
- REpresentational State Transfer (REST)
RPC
RPC通常被描述为单个URI,消费者可以在其上调用许多操作,一般仅通过POST。通常消费者传递一个结构化请求,其中包含要调用的操作名称以及希望传递给操作的任何参数,而响应将采用结构化格式。
POST /xml-rpc HTTP/1.1
Content-Type: text/xml
<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>status.create</methodName>
<params>
<param>
<value><string>First post!</string></value>
</param>
<param>
<value><string>mwop</string></value>
</param>
<param>
<value><dateTime.iso8601>20140328T15:22:21</dateTime.iso8601></value>
</param>
</params>
</methodCall>
上面的例程是POST到一个已知的url “/ xml-rpc”。payload包括要调用的操作,status.create以及要传递给它的参数。参数将按照定义的顺序传递,顺序通常很重要。在这种情况下,给定操作的处理程序在PHP中可能如下所示:
class Status
{
public function create($message, $user, $timestamp)
{
// do something...
}
}
RPC通常非常具有规律性,操作通常会很好地映射到函数定义。
上面例程的响应可以如下:
HTTP/1.1 200 OK
Content-Type: text/xml
<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
<params>
<param><value><struct>
<member>
<name>status</name>
<value><string>First post!</string></value>
</member>
<member>
<name>user</name>
<value><string>mwop</string></value>
</member>
<member>
<name>timestamp></name>
<value><dateTime.iso8601>20140328T15:22:21</dateTime.iso8601></value>
</member>
</struct></value></param>
</params>
</methodResponse>
我们收到单个值作为回应。在这种特殊情况下,我们收到一个返回的结构体,大致相当于一个匿名对象或一个关联数组。在PHP中,该值可能如下所示:
[
'status' => 'First post!',
'user' => 'mwop',
'timestamp' => '20140328T15:22:21',
]
当发生错误时,大多数已建立的RPC格式都有标准的报告方式;对于XML-RPC,这是一个“Fault”响应,SOAP有一个SOAP Fault。作为XML-RPC的一个例子,假设我们只将一个值传递给上面的服务;然后我们可能会得到如下的故障响应:
HTTP/1.1 200 OK
Content-Type: text/xml
<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
<fault><value><struct>
<member>
<name>faultCode</name>
<value><int>422</int></value>
</member>
<member>
<name>faultString</name>
<value><string>
Too few parameters passed; must include message, user, and timestamp
</string></value>
</member>
<member>
<name>timestamp></name>
<value><dateTime.iso8601>20140328T15:22:21</dateTime.iso8601></value>
</member>
</struct></value></fault>
</methodResponse>
需要注意的是RPC通常在响应体中进行所有错误报告; HTTP状态代码不会改变,这意味着您需要检查返回值以确定是否发生错误!
最后,许多RPC实现还通过协议本身为其最终用户提供文档。对于SOAP,这是WSDL;对于XML-RPC,这是通过各种“系统”方法。这种自我记录功能在实施时(并非总是如此!)可以为消费者提供有关如何与服务交互的宝贵信息。
关于RPC的要点是:
- 一个服务端点,许多操作
- 一个服务端点,一个HTTP方法(通常是POST)
- 结构化,可预测的请求格式,结构化,可预测的响应格式
- 结构化,可预测的错误报告格式
- 可用操作的结构化文档
总而言之,RPC通常不适合Web API:
- 无法通过URI确定可用的资源数量
- 缺少HTTP缓存,无法使用本机HTTP谓词进行常见操作;缺少用于错误报告的HTTP响应代码需要内省结果以确定是否发生错误
“一刀切”格式可能会受到限制;不能使用使用备用序列化格式的客户端,并且消息格式通常会对可以提交或返回的数据类型施加不必要的限制
REST
REpresentational State Transfer(REST)不是规范,而是围绕HTTP规范设计的体系结构。关于REST的维基百科文章提供了概念和丰富资源的出色概述。
REST利用HTTP的优势,并构建于:URI作为资源的唯一标识符
- 用于资源操作的丰富HTTP动词集
- 客户端能够指定它们可以呈现的表示格式,以及服务器是否可以表示这些格式(或指示它不能)
- 链接资源以指示关系(例如,超媒体链接,例如普通旧HTML文档中的链接!)
在谈论REST时,Richardson成熟度模型通常用于描述实现精心设计的REST API时所需的关注点。它由四个级别组成,从零开始索引:
- 级别0:HTTP作为远程过程调用的传输机制。从本质上讲,这是RPC,如上一节所述。 HTTP被用作隧道机制,所有服务都有一个入口点,并且没有利用HTTP的超媒体方面:URI来表示唯一资源,HTTP动词,指定和返回多种媒体类型的能力,或者服务之间的联系。 (注意:某些0级服务将使用多个HTTP谓词,但通常只混合使用POST(对于可能导致数据更改的操作)和GET(仅在获取数据时)
- 级别1:使用URI将各个资源表示为服务。这通过使用每个操作的URI或“资源”来区别于级别0(“URI”中的“R”代表“资源”毕竟!)。而不是/ services URI,每个服务都有一个:/ users,/ contacts等;此外,可能还允许通过唯一URI来解决服务中的各个项目:/ users / mwop将允许访问“mwop”用户。但是,在级别1,仍然没有很好地使用HTTP谓词,不同的媒体类型或服务之间的链接
- 第2级:使用HTTP谓词和标题进行资源交互。在1级构建时,2级服务开始使用全部HTTP请求方法:PATCH,PUT和DELETE被添加到库中。 GET用于不改变状态的安全操作,可以使用任意次数以获得相同的结果;换句话说,它可以使用HTTP请求缓存进行缓存。该服务根据错误类型返回错误的相应HTTP响应状态;没有200 OK,身体中嵌入了错误。 HTTP标头可用于改变响应;例如,可以基于Accept头返回不同的表示(并且Accept头用于请求替代表示而不是文件扩展,以便促进不管表示如何操纵相同资源的想法)
- 第3级:超媒体控制。大多数API设计师都达到了2级,感觉他们已经完成了自己的工作;他们有。但是,API的另一个方面可以使它更加可用:链接资源。请考虑这一点:您向API请求可用的事件票证。在级别2,响应可能是门票列表;但是,第3级更进一步,并提供每个票证资源的链接,以便可以保留其中任何一个!
基本上,一个很好的REST API:
- 对服务和这些服务公开的项使用唯一URI
- 使用全范围的HTTP谓词对这些资源执行操作,并使用全范围的HTTP来允许不同的内容表示,启用HTTP级别的缓存等
- 提供资源的关系链接,告诉消费者接下来可以做什么
所有这些理论都有助于告诉我们REST服务应该如何运作,但却很少告诉我们如何实现它们。设计REST更像是一种架构考虑因素。但是,这意味着API设计师现在必须做出大量选择:
- 你会公开什么样的表现形式?你如何报告无法满足给定表示的请求? REST没有规定任何特定格式
- 你将如何报告错误?同样,除了建议应该使用适当的HTTP响应代码之外,REST并没有规定任何特定的错误报告格式;然而,仅这些并没有提供足够的细节来对消费者有用
- 你将如何宣传哪种HTTP方法可用于给定资源?如果消费者使用你不支持的请求方法,你会怎么做?
- 你将如何处理身份验证等功能? Web API通常是无状态的,不应该依赖会话cookie等功能;消费者将如何在每个请求中提供凭据?你会使用HTTP身份验证还是OAuth2,还是创建API令牌?
- 你将如何处理超媒体链接?某些格式(如XML)本质上具有内置链接;其他的,如JSON,没有原生格式,这意味着你需要选择提供链接的方式。
- 你将如何记录可用的资源?与RPC不同,没有用于描述REST服务的“内置”机制;虽然超媒体链接会有所帮助,但消费者仍需要了解API的各种入口点。
这些都不是微不足道的问题,在很多情况下,你为一个人做出的选择会影响你为另一个人做出的选择。
简而言之,大多数REST提供了令人难以置信的灵活性和强大功能,但需要您做出许多选择,以便为消费者提供可靠的高质量体验。