Node.js 除了用来编写 WEB 应用之外,还可以用来编写 API 服务,我们在本文中会介绍编写 Node.js Rest API 的最佳实践,包括如何命名路由、进行认证和测试等话题,内容摘要如下:
1. 正确使用 HTTP Method 和路由试想你正要构建一个 API 用来创建、更新、获取、删除用户,对于这些操作,HTTP 规范里面已经有了现成的操作:POST、PUT、GET、DELETE,建议直接使用他们来描述接口的行为。 至于路由的命名,应该使用名词或名词性短语来作为资源标识符,比如上文提到的用户管理的例子,路由就应该长这样:
2. 正确的使用 HTTP 状态码如果服务器端在请求处理的过程中出错了,你必须设置正确的响应状态码,具体如下:
如果你使用 express,设置状态码非常简单:res.status(500).send({ error: 'Internal server error happend' }),如果使用了 restify,也是类似的:res.status(201)。 如果想看完整的 HTTP 状态码,点击这里。 3. 使用 HTTP Header 来发送元数据如果想要发送关于响应体数据的元数据,可以使用 Header ,Header 可以包含的常见元数据包括如下几类:
如果你需要在 Header 中发送自定义的元数据,最好的做法是在 Header 名称前面加 X,例如,需要发送 CSRF Token 的时候,实际的 Header 应该命名为:X-CSRF-Token,然而,这种 Header 在 RFC 6648 中已经被废弃了。API 在设置自定义 Header 的时候还要尽可能避免命名冲突,比如为了达到这个目的OpenStack 为所有 API 的自定义 Header 都加上了 OpenStack 的前缀: OpenStack-Identity-Account-ID
OpenStack-Networking-Host-Name
OpenStack-Object-Storage-Policy
需要注意的是,虽然 HTTP 规范中没有规定 Header 的大小,但是 Node.js 中 Header 的大小被**在了 80KB。官方原文如下:
4. 为 REST API 挑选合适的框架根据你的实际场景挑选合适的框架是非常重要的,Node.js 中的框架大致介绍如下: Express、Koa、HAPIExpress、Koa、HAPI 主要是用来构建浏览器 WEB 应用,因为他们都支持服务端模板渲染,虽然这只是他们众多功能中的一个。如果你的应用需要提供用户界面,那么这三个就是不错的选择。 Restify而 Restify 是专门用来创建符合 REST 规范的服务的,他诞生的目的就是帮你构建严格意义上的、可维护的 API 服务。Restify 内置了所有请求处理函数的 DTrace 支持。并且已经被 npm 和 netflix 用来在生产环境提供重要的服务。 5. 要对 API 进行黑盒测试测试 API 的最好办法是对他们进行黑盒测试,黑盒测试是一种不关心应用内部结构和工作原理的测试方法,测试时系统任何部分都不应该被 mock。 supertest 是可以用来对接口进行黑盒测试的模块之一,下面是基于测试框架 mocha 编写的一个测试用例,该用例的目的是检查接口是否能返回单条的用户数据: 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 服务所连接的数据库里面的数据是如何写进去的呢? 通常来说,你写测试的时候,要尽可能不对系统状态做假设,然而在某些场景下,你需要准确的知道系统当前所处的状态以增加更多的断言来提高测试覆盖率。如果你有这种需求,你可以试用如下的方法对数据库进行预填充:
此外,有了黑盒测试并不意味着不需要单元测试,针对 API 的单元测试还是需要编写的。 6. 使用基于 JWT 的无状态的认证机制因为 Rest API 必须是无状态的,因此认证机制也需要是无状态的,而基于 JWT(JSON Web Token) 的认证机制是无状态认证机制中的最佳解决方案。 JWT 的认证机制包含三部分:
为 API 添加基于 JWT 的认证机制也非常的简单,比如下面的代码: 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 的保护。如果要访问这种被保护的接口,需要使用 Authorization Header 来提供 token,比如: curl --Header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" my-website.com
你可能注意到了,JWT 模块并不依赖任何数据存储层,这是因为 token 本身是可以单独被校验的,token 里面的 payload 甚至可以包含 token 的签名时间、有效期限。 此外,你还需要确保,所有的 API 接口只能通过更安全的 HTTPS 链接来访问。 7. 学会使用条件请求机制条件请求机制是基于不同的 Header 表现出不同的行为的机制,可以认为这些 Header 就是请求处理方式的先决条件,如果条件满足,请求处理方式就会有所不同。 可以利用这些 Header 检测服务器上的资源版本是否匹配特定的资源版本,这些 Header 的取值可以是如下的内容:
具体来说:
下面来看一个实际的例子: 客户端不知道 doc 资源的任何版本,所以请求时即不能提供 If-Modified-Since,也不能提供 If-Non-Match 两个 Header,然后服务端在响应中会增加 Etag 和 Last-Modified 两个 Header。 nodejs-resftul-api-with-conditional-request-without-previous-versions.png 接下来,客户端再次请求相同的资源的时候,就可以带上 If-Modified-Since 和 If-Non-Match 这两个 Header 了,然后如果服务器端会检查资源是否修改,如果没有修改,直接返回 304 - Not Modified 状态码,而不重复发送资源的内容。 nodejs-resftul-api-with-conditional-request-with-previous-versions.png 8. 拥抱接口调用频率**(Rate-Limiting)频率**是用来控制调用方有对接口发起请求的次数,为了让你的 API 用户知道他们还剩下多少余额,可以设置下面的 Header:
大多数的 WEB 框架都支持上面这些 Header,如果内置不支持,也可以找到插件来支持,比如,如果你使用了 koa,可以使用 koa-rate-limit。 需要注意的是,不同的 API 服务提供商频率**的时间窗差异会很大,比如 GitHub 是 60 分钟,而 Twitter 是 15 分钟。 9. 编写良好的 API 文档编写 API 的目的当然是让别人使用并受益,提供良好的接口文档至关重要。下面这两个开源项目可以帮你创建 API 文档:
如果你愿意使用第三方文档服务商,可以考虑 Apiary。 10. 对 API 技术演化保持关注过去几年中,API 技术方案领域出现了两种新的查询语言,分别是 Facebook 的 GraphQL 和 Netflix 的 Falcor,为什么需要他们呢? 试想这种 API 接口请求:/org/1/space/2/docs/1/collaborators?include=email |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|