restful api编写规范
Node.js 除了用来编写 WEB 应用之外,还可以用来编写 API 服务,我们在本文中会介绍编写 Node.js Rest API 的最佳实践,包括如何命名路由、进行认证和测试等话题,内容摘要如下:
正确使用
HTTP Method和路由正确的使用
HTTP状态码使用
HTTP Header来发送元数据为
REST API挑选合适的框架要对
API进行黑盒测试使用基于
JWT的无状态的认证机制学会使用条件请求机制
拥抱接口调用频率限制(Rate-Limiting)
编写良好的
API文档对
API技术演化保持关注
1. 正确使用 HTTP Method 和路由
试想你正要构建一个 API 用来创建、更新、获取、删除用户,对于这些操作,HTTP 规范里面已经有了现成的操作:POST、PUT、GET、DELETE,建议直接使用他们来描述接口的行为。
至于路由的命名,应该使用名词或名词性短语来作为资源标识符,比如上文提到的用户管理的例子,路由就应该长这样:
POST /users或者PUT /users/:id用来创建新用户;GET /users用来获取用户列表;GET /users/:id用来获取单个用户;PATCH /users/:id用来更新用户信息;DELETE /users/:id用来删除用户;
2. 正确的使用 HTTP 状态码
如果服务器端在请求处理的过程中出错了,你必须设置正确的响应状态码,具体如下:
2xx,表示一切正常;3xx,表示资源位置已经更改;4xx,表示因为客户端错误而导致请求无法被处理,比如参数校验没通过;5xx,表示因为服务器错误导致请求无法被处理,比如服务端抛了异常;
如果你使用 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。官方原文如下:
不要让 HTTP
Header,包括其中状态码那行的整体大小超过 HTTP_MAX_Header_SIZE,这样做的目的是为了防御基于Header的DDOS攻击。点击这里
4. 为 REST API 挑选合适的框架
根据你的实际场景挑选合适的框架是非常重要的,Node.js 中的框架大致介绍如下:
Express、Koa、HAPI
Express、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 的认证机制包含三部分:
Header:包含token的类型和哈希算法;payload:包含声明信息;signature:JWT实际上并不是对payload进行加密,只是对其做了签名;
为 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: BearereyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" my-website.com
你可能注意到了,JWT 模块并不依赖任何数据存储层,这是因为 token 本身是可以单独被校验的,token 里面的 payload 甚至可以包含 token 的签名时间、有效期限。
此外,你还需要确保,所有的 API 接口只能通过更安全的 HTTPS 链接来访问。
7. 学会使用条件请求机制
条件请求机制是基于不同的 Header 表现出不同的行为的机制,可以认为这些 Header 就是请求处理方式的先决条件,如果条件满足,请求处理方式就会有所不同。
可以利用这些 Header 检测服务器上的资源版本是否匹配特定的资源版本,这些 Header 的取值可以是如下的内容:
资源的最后修改时间;
资源的标签(随资源变化而变化);
具体来说:
Last-Modified:标识资源的最新修改时间;Etag:标识资源的标签;If-Modified-Since:结合Last-Modified Header使用;If-Non-Match:结合Etag使用;
下面来看一个实际的例子:
客户端不知道 doc 资源的任何版本,所以请求时即不能提供 If-Modified-Since,也不能提供 If-Non-Match 两个 Header,然后服务端在响应中会增加 Etag 和 Last-Modified 两个 Header。
接下来,客户端再次请求相同的资源的时候,就可以带上 If-Modified-Since 和 If-Non-Match 这两个 Header 了,然后如果服务器端会检查资源是否修改,如果没有修改,直接返回 304 - Not Modified 状态码,而不重复发送资源的内容。
8. 拥抱接口调用频率限制(Rate-Limiting)
频率限制是用来控制调用方有对接口发起请求的次数,为了让你的 API 用户知道他们还剩下多少余额,可以设置下面的 Header:
X-Rate-Limit-Limit:特定时间段内允许的最多请求次数;
X-Rate-Limit-Remaining:特定时间段内剩余的请求次数;
X-Rate-Limit-Reset:什么时候请求频率限制次数会重置;
大多数的 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&page=1&limit=10,类似的情况会让 API 很快失控,如果你希望所有接口能返回类似的响应格式,那么 GraphQL 和 Falcor 就能帮你解决这个问题。
关于 GraphQL:
GraphQL 是一种用于 API 的查询语言,也是一种基于现有数据处理数据查询的运行时。GraphQL 为您的 API 中的数据提供了一个完整和可理解的描述,使用户能够准确地询问他们需要什么,使得随着时间推移的 API 演化更容易,GraphQL 还有强大的开发工具支持。 到这里阅读更多。
关于 Falcor:
Falcor 是支撑着 Netflix UI 的创新数据平台。Falcor 允许你将所有后端数据建模为 Node.js 服务商的单个虚拟 JSON 对象。在客户端可以使用熟悉的 JavaScript 操作、处理远程JSON对象。如果你知道你的数据,你就知道你的 API 长啥样。 到这里阅读更多。
能带来灵感的优秀 API 设计
如果你正在开发 Rest API 或者准备改进老版本的 API,这里收集了几个在线上提供服务、设计优秀并且非常直接借鉴的 API:
本文收藏于 https://segmentfault.com/a/1190000008537712
restful api编写规范的更多相关文章
- RESTful API 编写规范
RESTful API 编写规范 在一个RESTful系统里,客户端向服务端发起索取资源的操作只能通过HTTP协议语义来进行交互.最常用的HTTP协议语义有以下5个: GET GET:发送一条或者多条 ...
- RESTful API 编写指南
基于一些不错的RESTful开发组件,可以快速的开发出不错的RESTful API,但如果不了解开发规范的.健壮的RESTful API的基本面,即便优秀的RESTful开发组件摆在面前,也无法很好的 ...
- RESTful API接口文档规范小坑
希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. 前后端分离的开发模式,假如使用的是基于RESTful API的七层通讯协议,在联调的时候,如何避免配合过程中出现问 ...
- 深入理解 RESTful Api 架构
转自https://mengkang.net/620.html 一些常见的误解 不要以为 RESTful Api 就是设计得像便于 SEO 的伪静态,例如一个 Api 的 URL 类似于 http: ...
- RESTful API 和 Django REST framework
100天 cmdb最后一天 #RESTful API - 定义规范 如get就是请求题 - 面向资源编程 把网络任何东西都当作资源 #给一个url,根据方法的不同对资源做不同的操作 #返回结果和状态码 ...
- 使用Spring MVC开发RESTful API
第3章 使用Spring MVC开发RESTful API Restful简介 第一印象 左侧是传统写法,右侧是RESTful写法 用url描述资源,而不是行为 用http方法描述行为,使用http状 ...
- 理解RESTful Api设计
REST REST(REpresentational State Transfer)是 Roy Fielding 博士于 2000 年在他的博士论文中提出来的一种软件架构风格(一组架构约束条件和原则) ...
- 利用 Django REST framework 编写 RESTful API
利用 Django REST framework 编写 RESTful API Updateat 2015/12/3: 增加 filter 最近在玩 Django,不得不说 rest_framewor ...
- Spring Boot 2.x 编写 RESTful API (三) 程序层次 & 数据传输
用Spring Boot编写RESTful API 学习笔记 程序的层次结构 相邻层级的数据传输 JavaBean 有一个 public 的无参构造方法 属性 private,且可以通过 get.se ...
随机推荐
- 内置的HTTP服务器【Modern PHP】
目录 启动服务器 配置服务器 路由器脚本 判断是否为内置的服务器 PHP5.4.0起,PHP内置了Web服务器.对本地开发是个极好的工具,便捷,无需安装WAMP.XAMP或大新那个web服务器,就能在 ...
- Java 封装(内部类)
1.封装 封装是指,一种将抽象性函式接口的实例细节部份包装.隐藏起来的方法.封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问.要访问该类的代码和数据,必须通过严格的接口控制 ...
- Entity Framework 指定架构无效 错误:1052
IIS发布网站:如果不发布放到IIS没有问题,发布后IIS部署 打开网站却提示指定架构无效 1052 找到很多解决的问题 1添加wenconfig 2.更改entity名的 其实我认为最简单的就是先找 ...
- android--简单的发短信功能
一.准备字符资源 <string name="tip_phone">请输入电话号码</string> <string name="tip_s ...
- 二值形态学——腐蚀与膨胀 及 C语言代码实现
参考文献:数字图像处理(第三版) 何东健 西安电子科技大学出版社 二值形态学中的运算对象是集合, 但实际运算中, 当涉及两个集合时并不把它们看作是互相对等的. 一般设A为图像集合, S为结构元素, 数 ...
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
- Android如何自学----转自lavor从segmentfault
如何自学Android 1. Java知识储备 本知识点不做重点讲解: 对于有基础的同学推荐看<Java编程思想>,巩固基础,查漏补全,了解并熟悉更多细节知识点. 对于没有基础的同学推荐看 ...
- [POI2015]LOG
题目 发现询问是针对整个区间,也就是说位置什么用都没有 发现我们需要构造出\(s\)个长度为\(c\)的数列,每个数只能在一个数列中出现一次,且一个数最多的使用次数是其大小 对于那些大于等于\(s\) ...
- POJ3304 Segments
嘟嘟嘟 题面就不说了,网上都有. 刚开始理解成了只要有不孤立的线段就算合法,结果就不会了--然而题中要求是所有线段至少有一个交点. 其实想一想就知道,问题转化为了是否存在一条直线和所有线段都有交点. ...
- Cookies、sessionStorage和localStorage解释及区别?
浏览器的缓存机制提供了可以将用户数据存储在客户端上的方式,可以利用cookie,session等跟服务器端进行数据交互 一.cookie和session Cookie和 session都是用来跟踪浏览 ...