在这篇文章里,我们将介绍 Node.js REST API 的最佳实践,包括关于路由命名,身份认证,黑盒测试,使用恰当的网络缓存等内容。
一个最流行的 Node.js RESTful API 监听工具 ,通过 Trace,我们帮助我们的用户寻找程序中的问题。我们的经验告诉我们,开发者开发 REST API 时有很多问题。
我希望这些用在 上的这些最佳实践能够帮助大家。
想象一下,你写的 Node.js RESTful API 可以用于新建,更新,检索或者删除用户。 对于这些业务操作,HTTP 已经有了成套的工具箱:
,
- POST
,
- PUT
,
- GET
或
- PATCH
。
- DELETE
作为最佳实践,你的 API 路由应该使用名词作为资源标识符 。说到关于用户的资源,你可以像下面这样构建:
或
- POST /user
创建新用户
- PUT /user:/id
查找一些用户
- GET /user
查找某个用户
- GET /user/:id
编辑修改一个存在用户的信息
- PATCH /user/:id
删除用户
- DELETE /user/:id
当请求出现错误的时候,你必须返回相应的状态码:
, 一切正常
- 2xx
, 资源被移除
- 3xx
, 客户端错误
- 4xx
, API(服务端)错误
- 5xx
如果你使用 Express,设置状态码非常简单
,Restify 框架的写法也差不多
- res.status(500).send({error: 'Internal server error happened'})
。
- res.status(201)
如果想查看完整版 ,点击
在 HTTP header 上添加关于有效载荷(payload)的元数据(metadata),适用于以下场景:
一系列的标准化 HTTP header 可参照
如果你需要在你的 HTTP header 里设置一些定制的元数据,最佳实践是在定制内容前加
。 举个例子,如果你在使用 CSRF token,通用的命名方法 (不是规范) 是命名为
- X
。当然通过 的方式被弃用。创建的 API 时,要尽最大的可能避免命名冲突。比如,OpenStack 在命名 HTTP header 时,以
- X-Csrf-Token
开头:
- OpenStack
- OpenStack-Identity-Account-ID
- OpenStack-Networking-Host-Name
- OpenStack-Object-Storage-Policy
however, Node.js (as of writing this article) imposes an 80KB size limit on the headers object for practical reasons.
注意,HTTP 规范并没有对 HTTP header 大小进行限制;尽管如此,出于对应用场景的考虑,作者希望对 HTTP header 进行 80KB 的大小限制。
"不要允许设置任意大小的 HTTP header (包括状态行) ,不要超过
的限制。这种检验师为了保护程序,防止'阻断服务攻击(denial-of-service attack)',攻击者会填入一个无法加载完的请求头,让程序进入一个永远在加载中的状态。"
- HTTP_MAX_HEADER_SIZE
选自
选择最适合项目应用场景的,才是最重要的。
, 和 都可以支持浏览器调用,并且他们都支持模板和后端渲染,这里我们之罗列一小部分他们的特性。如果你的应用需要面向用户(界面),这些特性对他们是有意义的。
另一面, 是一个专注于构建 REST 服务的库。它强制你使用严格模式构建你的 API 服务,以此保证整个系统的可维护性和可监控性。 Restify 也带有自动化工具 支持,动态监测你的程序。
Restify 也被大量产品用在主程序里,比如 和 。
检验你的 REST API 的最佳方式是进行黑盒测试。
黑盒测试是一种测试方法,就是排除任何主观因素和已知条件,检测每个功能是否都能正常使用。所以没有任何外部依赖是假数据或者桩代码(stub),整个程序是看一个整体。
一个可以帮助你进行 Node.js REST API 黑盒测试的工具 。
一个简单的用来检验用户返回数据的用例,如果使用 完成的话,如下:
- const request = require('supertest')
- describe('GET /user/:id', function() {
- it('returns a user', function() {
- // newer mocha versions accepts promises as well
- return request(app)
- .get('/user')
- .set('Accept', 'application/json')
- .expect(200, {
- id: '1',
- name: 'John Math'
- }, done)
- })
- })
通常,一个好的写测试用例的方法,就是尽可能减少假设的状态。并且,在某些场景中,当你需要知道系统状态的时候,黑盒测试可以发现系统设计的盲点,所以你可以通过断言,实现更高的测试覆盖率。
所以,根据你的需要,可以用下列方式之一,用测试数据填充数据库:
当然,黑盒测试不意味这你不需要单元测试,你也需要给你的 API 写单元测试脚本 。
你的 REST API 必须是无状态的,身份认证也一样。JWT (JSON web Token) 是个经典的解决方案。
JWT 由三部分构成:
在你的程序中加入 JWT-based 非常简单:
- const koa = require('koa')
- const jwt = require('koa-jwt')
- const app = koa()
- app.use(jwt({
- secret: 'very-secret'
- }))
- // Protected middleware
- app.use(function *(){
- // content of the token will be available on this.state.user
- this.body = {
- secret: '42'
- }
- })
之后,在客户端调的 API 会受到 JWT 保护。访问受保护的端,你必须在请求头
字段提供 token。
- Authorization
- curl --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" my-website.com`
你可能注意到了,JWT 不依赖任何数据层。因为 JWT 可以通过 tokens 自我验证,请求也可以包含请求有效时间。
推荐一篇有价值的文章, 。
You can think of these headers as preconditions: if they are met, the requests will be executed in a different way.
条件请求根据 HTTP header 不同,返回不同。你可以把这些 HTTP header 作为先决条件:当条件符合,就会获得相应的返回。
These headers try to check whether a version of a resource stored on the server matches a given version of the same resource. Because of this reason, these headers can be:
这些 header 希望检测,这一版本的资源是否存在服务端,并返回对应版本的资源。所以,header 可能包括如下信息:
对应 API:
(标识当前资源最后修改时间)
- Last-Modified
(和
- If-Modified-Since
一起使用)
- Last-Modified
(和
- If-None-Match
一起使用)
- Etag
The client below did not have any previous versions of the
resource, so neither the
- doc
, nor the
- If-Modified-Since
header was applied when the resource was sent. Then, the server responds with the
- If-None-Match
and
- Etag
headers properly set.
- Last-Modified
下面,客户端先前没有任何关于
资源的版本,所以在发送请求时没有
- doc
, 和
- If-Modified-Since
信息。之后服务端返回资源,并在响应头里写
- If-None-Match
和
- Etag
。
- Last-Modified
引自:
如果在请求的时候,客户端设置了
和
- If-Modified-Since
,一旦请求这个资源, 这个验证这个资源的版本。如果版本相同,服务器只是回应
- If-None-Match
状态,不返回其他信息。
- 304 - Not Modified
引自
请求频率限制用于控制 API 可以被消费多少次。
可以通过设置 HTTP header 告诉客户端还有多少请求可以被消费:
同一个时间段所允许的请求的最大数目
- X-Rate-Limit-Limit
在当前时间段内剩余的请求的数量
- X-Rate-Limit-Remaining
为了得到最大请求数所等待的秒数
- X-Rate-Limit-Reset
大部分 HTTP 框架支持这种写法 (或通过插件支持)。举个例子,如果你用 Koa,可以使用 这个包。
注意,请求时间间隔可以根据 API 不同,设置不同。比如 GitHub 时间间隔是 1 小时,Twitter 是 15 分钟。
你写的 API 是给别人用的,要对别人有价值。提供一个 API 文档是十分重要的。
以下开源项目可以帮助你创建 API 文档:
如果你想使用 API 自动生成工具,推荐 。
在过去的几年中,出现了两个 API 查询语言,Facebook 的 GraphQL 和 Netflix 的 Falcor。但我们为什么需要它们?
想象以下 RESTful 资源请求:
- /org/1/space/2/docs/1/collaborators?include=email&page=1&limit=10`
这很容易失控——如果你希望按照相同的数据结构返回数据。这是 GraphQL 和 Falcor 解决的问题。
GraphQL 是一门 API 查询语言,运行时为完成这些查询现有数据。GraphQL 提供了一套完整的,易于理解的,可以描述数据的 API。让客户端有了完整描述自己需要的数据的能力,随着时间的推移,这种方式可能演变成 API,成为更强大的开发工具。
Falcor 一个创新的数据抓取哭,支撑着 Netflix UI。Falcor 允许你通过一个 Node 服务上的虚拟 JSON 操作任何后台数据。在客户端,使用远程 JSON 对象,通过 get,set, call 查找数据,数据就是 API 。
如果你正想要开发 Node.js REST API 或者更新一个版本,我们收集了 4 个有价值的,现实中的例子:
我希望此刻你知道应该怎么构建 Node.js API,如果我漏写了什么,请在评论区告知。
扫码关注 w3ctech 微信公众号
来源: http://www.tuicool.com/articles/auiEzqn