设计RESTful Web API最佳实践

RESTful API被广泛应用于各大互联网公司,诸如Twitter、Facebook等。那么,我们如何正确打造一款Web API呢?

首先,我们从宏观上强调一下RESTful API的设计原则!如下:

  • 无状态的设计: 数据不会存储在session中,每一次请求都包含了服务器端和客户端需要的数据。
  • 简单易懂的文档:花费最少的时间读文档就能读懂每个请求和响应都包含哪些内容。
  • 语义:API应该使用已有的HTTP协议,必须符合语义。例如:请求类型,状态码以及HTTP验证。

我们在设计WEB API时,应该将以上3条牢记心中。接着,我们从6个方面来看看实践。

输出格式:

选择输出的格式,不仅仅是JSON,也可以是XML或者CSV。

你也可以考虑添加多种格式支持。最好的处理方式就是查看请求的HEADER中的MIME-TYPE来返回对应的格式。

GET /v1/putty HTTP/1.1 GET /v1/putty HTTP/1.1
Host: api.putty.biz
Accept: application/json
Host: api.putty.biz
Accept: application/xml
返回JSON数据 返回XML格式数据

版本:

这也是一个非常重要的方面。延用github的方法,如下:

GET /v1/putty HTTP/1.1 GET /v2/putty HTTP/1.1
Host: api.putty.biz Host: api.putty.biz
从版本1的端点返回结果 从版本2的端点返回结果

URL结构:

路径(endpoint)是设计API最重要的部分之一。花费时间去定义一个正确的端点可以让我们的API更加易懂和可预见的。

URLs应该短小且具有描述性,利用路径来划分结构。

如果你正在与对象工作,将对象id放置在id中,然后将其他都作为查询字符串。

我们看一看一个商店定位器的API, 以下是一些可能的endpoints。

  • /v1/stores/1234 – 返回id为1234的店铺
  • /v1/stores/1234/report – 报告一个关于店铺id是1234的错误
  • /v1/stores – 返回所有店铺
  • /v1/stores/near?lat=12.34&lon=-12.34  – 返回在某个坐标位置附近的店铺
  • /v1/categories – 返回一个店铺分类列表

如果你允许修改一个对象,请通过HTTP verbs来操作。通过将正确的请求类型分配给已有的URL来实现, 你就可以实现符合语义的CRUD操作。

HTTP Verb Description
GET 这是最常用的verb, 用于取回数据,例如:GET /v1/stores/1234
PUT PUT请求往往用于更新或者替换一个对象,例如: PUT /v1/stores/1234
POST POST请求用于新建一个对象实例,例如: POST /v1/stores
DELETE 删除一个对象,例如:DELETE /v1/stores/1234

验证:

市面上存在着许多验证的实现方案,下面列举3个:

  1. OAuth: 如果你的API是基于用户的, OAuth是比较好的选择之一。可能你刚接触到它,会感觉比较复杂难搞,但去尝试一下吧。它是使用最广泛(从而被验证的最多)和安全的方案。
  2. HTTP Basic Authentication: 如果你只是需要一个简单的密码来验证API有效请求有效性,那么可以使用HTTP Basic Authentication.
  3. API Key: 基于API KEY的验证方式相对比较简单。但是,有些资料提及将API KEY与HTTP Basic Authentication结合使用,个人建议是不要这样操作。取而代之:将api key作为查询字符串,附加到URL中。例如:api.putty.biz/v1/geocode?q=test&api_key=YOUR_API_KEY. 这样做的好处是用户调试的时候可以在浏览器中直接进行啦。所以我也不建议将API key附加到HEADER中去。

时间戳:

时间戳是设计API时比较容易踩倒的一个坑。我建议不要去使用Unix时间戳,因为它不易读。不相信,您可以读一读FaceBook的故事

另外一个大家更加容易接受的时间格式是ISO-8601.它更容易读,也更加容易被解析,还支持时区。

错误处理:

最后,我们来谈谈错误处理。在一个API中有许多不同类型的错误,包括授权错误(例如:您不允许删除这个商店), 传参验证错误(请提供商店的名称), 未找到错误和500内部错误的消息。

首先,我们必须返回符合语义的HTTP状态码。例如:没有错误会返回200 OK。授权错误应该返回403 forbidden, 传参错误应该返回422(unprocessable entity). 更多状态码说明,请参考httpstatuses.com(简单明了)。

接着,是我们返回的错误消息。有时候我们必须给出更为详细一些的描述性语句,告诉调用者发生了什么。我建议返回一个包含error属性的对象,属性值即为消息正文。

总结:

我们设计API的时候,需要考虑的方面很多,可以采取的方案也不同,这最终取决于个人喜好。

以上内容相对比较简单,而且还有一些方面并未提及,建议您可以继续读一读阮一峰大大的《RESTful设计指南》