Node.js REST API的10个最佳实践

18-10-06 banq
                   

在本文中,我们将介绍编写Node.js REST API的最佳实践,包括命名路由,身份验证,黑盒测试以及为这些资源使用适当的缓存头等主题。

#1 - 使用HTTP方法和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 删除用户

“API路由应始终使用名词作为资源标识符!”


#2 - 正确使用HTTP状态代码

如果在提供请求时出现问题,必须在响应中为其设置正确的状态代码:

2xx 一切都好,
3xx,资源被移动,
4xx,由于客户端错误(如请求不存在的资源)而无法满足请求,
5xx,API方面出现问题(就像发生异常一样)。

如果使用Express,设置状态代码就像res.status(500).send({error: 'Internal server error happened'})。
Restify相似:res.status(201)。


#3 - 使用HTTP标头发送元数据

要附加有关要发送的有效负荷内容的元数据,请使用HTTP标头。像这样的标题可以是以下信息:

分页,
限速,
或认证。

可以在此处找到标准化HTTP标头的列表

如果需要在标题中设置任​​何自定义元数据,最佳做法是为它们添加前缀X。例如,如果使用的是CSRF令牌,那么将它们命名为常用(但非标准)方式X-Csrf-Token。然而,对于RFC 6648,他们已被弃用。新API应尽最大努力不使用可能与其他应用程序冲突的标头名称。例如,OpenStack为其标头添加前缀OpenStack:

OpenStack-Identity-Account-ID
OpenStack-Networking-Host-Name
OpenStack-Object-Storage-Policy

请注意,HTTP标准没有定义标头的任何大小限制; 但是,出于实际原因,Node.js(撰写本文时)对头对象施加了80KB的大小限制。

“不要允许HTTP标头(包括状态行)的总大小超过HTTP_MAX_HEADER_SIZE。此检查用于保护嵌入器免受拒绝服务攻击“。
来自Node.js HTTP解析器

#4 - 为Node.js REST API选择正确的框架

选择最适合用例的框架非常重要。

Express,Koa或Hapi
Express,Koa和Hapi可用于创建浏览器应用程序,它们还支持模板和渲染 。

RESTify
另一方面,Restify专注于帮助您构建REST服务。它的存在是为了让您构建可维护和可观察的“严格”API服务。Restify还为所有处理程序提供自动DTrace支持。

Restify用于主要应用程序(如npm或Netflix)的生产中。

“Restify的存在是为了让你构建可维护和可观察的”严格“API服务。”


#5 - 黑盒测试你的Node.js REST API

测试REST API的最佳方法之一是将它们视为黑盒子。

黑盒测试是一种测试应用程序功能的方法,不需要了解其内部结构或工作原理。因此,没有任何依赖项被模拟或存根,但系统作为一个整体进行测试。

其中一个可以帮助您进行黑盒测试Node.js REST API的模块是最优秀的。

检查用户是否使用测试运行器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)
})
})


您可能会问:数据如何填充到为REST API提供服务的数据库中?

一般来说,以一种尽可能少的关于系统状态的假设的方式编写测试是一种很好的方法。可以使用以下方法之一使用测试数据填充数据库:

在已知的生产数据子集上运行黑盒测试场景,

在运行测试用例之前,使用精心设计的数据填充数据库。

当然,黑盒测试并不意味着你不必进行单元测试,你仍然需要为你的API 编写单元测试。

#6 - 基于JWT的无状态认证

由于REST API必须是无状态的,因此身份验证层也是如此。为此,JWT (JSON Web Token)是理想的选择。

JWT由三部分组成:

标头,包含令牌类型和散列算法
有效负载payload,包含声明定义
签名 ,(JWT不加密有效载荷,只需签名!)

将基于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'
}
})


要访问上面这个受保护的端点,必须在Authorization标头字段中提供令牌。

curl --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" my-website.com

注意JWT模块不依赖于任何数据库层。因为所有JWT令牌都可以自己验证,并且它们也可以包含生存时间值。

此外,始终必须确保只能通过使用HTTPS的安全连接访问所有API端点。


#7 - 使用条件请求

条件请求是HTTP请求根据特定的HTTP标头执行不同的HTTP请求。可以将这些标头视为先决条件:如果满足这些标头,请求将以不同的方式执行。

根据特定的HTTP标头执行不同的条件请求

<p class="click-to-tweet-button">
<a href="https://twitter.com/share?text=%22Conditional%20requests%20are%20executed%20differently%20depending%20on%20specific%20HTTP%20headers%22%20via%20%40RisingStack;url=https://blog.risingstack.com/10-best-practices-for-writing-node-js-rest-apis" target="_blank" c>Click To Tweet</a>
</p>

这些标头尝试检查存储在服务器上的资源版本是否与同一资源的指定版本匹配。由于这个原因,这些标题可以是:

上次修改的时间戳,
或实体标签,每个版本都有所不同。

这些标题可以是:

Last-Modified (表示上次修改资源的时间),
Etag (表示实体标签),
If-Modified-Since (与Last-Modified标题一起使用),
If-None-Match (与Etag标题一起使用),



#8 - 拥抱速率限制

速率限制用于控制指定消费者可以向API发送的请求数量。

要告诉你的API用户他们剩下多少请求,请设置以下标头:

X-Rate-Limit-Limit,给定时间间隔内允许的请求数
X-Rate-Limit-Remaining,在同一间隔内剩余的请求数,
X-Rate-Limit-Reset,速率限制将被重置的时间。
大多数HTTP框架都支持开箱即用(或使用插件)。例如,如果您使用Koa,则有koa-ratelimit包。

请注意,时间窗口可能因不同的API提供程序而异 - 例如,GitHub使用一小时,而Twitter则为15分钟。

#9 - 创建适当的API文档

编写API文档以便其他人可以使用它们,从中受益。为Node.js REST API提供API文档至关重要。

以下两个开源项目是可以帮助您为API创建文档:

API蓝图
swagger


#10 - 不要错过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
Falcor是为Netflix用户界面提供支持的创新数据平台。Falcor允许您将所有后端数据建模为节点服务器上的单个Virtual JSON对象。在客户端上,您可以使用熟悉的JavaScript操作(如get,set和call)来处理远程JSON对象。如果您了解自己的数据,就会知道自己的API。

原文

                   

1