REST
REST是REpresentational State Transfer(表述性状态转移)的缩写。
Restify
restify
是一个 NodeJS
模块,可以让你创建正确的 REST Web Services
。它借鉴了很多 express
的设计,restify
比起express
更专注于REST
服务,去掉了express
中的template
, render
等功能,同时强化了REST
协议使用,并且提供了版本化支持,HTTP的异常处理等。此外 restify
还提供了 DTrace
功能,为程序调式带来新的便利!
安装 restify
安装restify,先创建目录,然后使用npm安装即可:
1
2
3
4
mkdir restify-restful
cd restify-restful
npm init -y
npm install restify
Hello World
下面的代码就是restify的入门程序:
1
2
3
4
5
6
7
8
9
const restify = require('restify');
const server = restify.createServer()
server.get('/', (req, res, next)=>{
res.send("hello world");
return next();
})
server.listen(3001, '127.0.0.1', function () {
console.log('%s listening at %s', server.name, server.url)
});
响应处理链
对于每个 HTTP 请求,restify 通过一个响应处理链来对请求进行处理。restify 中有三种不同的处理链。
- pre:在确定路由之前执行的处理器链。
- use:在确定路由之后执行的处理器链。
- {httpVerb}:一个路由独有的处理器链。
通过 restify 服务器的 pre()方法可以注册处理器到 pre 处理器链中。那么对所有接收的 HTTP 请求,都会预先调用该处理器链中的处理器。处理器链的执行发生在确定路由之前,因此即便是没有路由与请求相对应,也会调用该处理器链。该处理器链适合执行日志记录、性能指标数据采集和 HTTP 请求清理等工作。
1
2
3
4
5
6
7
8
9
10
11
12
const restify = require('restify');
const server = restify.createServer()
server.pre(restify.pre.dedupeSlashes()) // 去除请求网址中的多个 /
server.get('/', (req, res, next) => {
res.send("hello world");
return next();
})
server.listen(3001, '127.0.0.1', function () {
console.log('%s listening at %s', server.name, server.url)
});
通过 restify 服务器的 use()方法可以注册处理器到 use 处理器链中。该处理器链在选中了一个路由来处理请求之后被调用,但是发生在实际的路由处理逻辑之前。也就是说如果你没有定义的路由但是却被请求者请求了,那么pre会处理该请求,但是use处理链不会处理该请求。
该处理器链适合执行用户认证、应用相关的请求清理和响应格式化等工作。典型的应用场景包括检查用户是否完成认证,对请求和响应的 HTTP 头进行修改等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const restify = require('restify');
const server = restify.createServer()
server.use(function (req, res, next) {
console.log('use', req.url);
return next()
})
server.get('/', (req, res, next) => {
res.send("hello world");
return next();
})
server.listen(3001, '127.0.0.1', function () {
console.log('%s listening at %s', server.name, server.url)
});
在每个处理器的实现中,应该在合适的时机调用 next()方法来把处理流程转交给处理器链中的下一个处理器。具体的时机由每个处理器实现根据需要来决定。这给处理 HTTP 响应带来了极大的灵活性,也使得处理器可以被有效复用。
路由
restify 的路由表示的是对 HTTP 请求的处理逻辑。一个路由有 3 个部分:分别是 HTTP 动词、匹配条件和处理方法。restify 服务器对象可以使用方法 get、put、post、del、head、patch 和 opts,分别与名称相同的 HTTP 请求动词相对应。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const restify = require('restify');
const server = restify.createServer();
const logHandler = (req, res, next) => {
console.log('req: %s, params: %s', req.href(), JSON.stringify(req.params));
res.send(req.params);
return next();
};
server.get('/route/simple', logHandler);
server.get('/route/user/:id', logHandler);
server.get(/^\/route\/order\/(\d+)/, logHandler);
server.get({
path: '/route/versioned',
version: '1.0.0'
}, logHandler);
server.listen(8000, () => console.log('%s listening at %s', server.name, server.url));
多版本路由
REST API 通常有同时运行多个版本的要求,以支持 API 的演化。restify 内置提供了基于语义化版本号(semver)规范的多版本支持。在 HTTP 请求中可以使用 HTTP 请求头 Accept-Version 来指定版本号。每个路由可以按照下面的代码中的方式,在属性 version 中指定该路由的一个或多个版本号。如果请求中不包含 HTTP 头 Accept-Version,那么会匹配同一路由中版本最高的那一个。否则,就按照由 Accept-Version 指定的版本号来进行匹配,并调用匹配版本的路由。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var restify = require('restify');
var server = restify.createServer();
function sendV1(req, res, next) {
console.log('sendV1', req.params.name);
res.send({sendV1: req.params.name});
return next();
}
function sendV2(req, res, next) {
console.log('sendV2', req.params.name);
res.send({sendV2: req.params.name});
return next();
}
server.get('/hello/:name', restify.plugins.conditionalHandler([
{version: '1.1.3', handler: sendV1},
{version: ['2.0.0', '2.1.0'], handler: sendV2}, // 默认返还最高版本
]));
server.listen(3001, '127.0.0.1', () => console.log('%s listening at %s', server.name, server.url));
错误处理
由于 HTTP 的状态码是标准的,restify 提供了一个专门的模块 restify-errors
来创建对应不同状态码的 Error 对象的处理,可以直接在 send() 方法中使用。restify 中产生的错误会作为事件来发送,可以使用 Node.js 标准的事件处理机制来进行处理。需要注意的是,只有使用 next()方法发送的错误会被作为事件来发送,使用响应对象的 send()方法发送的则不会。
插件
在 REST API 的开发中,某些任务是比较常见的。restify 提供了一系列插件来满足这些通用的需求。这些插件可以通过 restify.plugins
来访问,并使用 use()方法来注册。例如在下面的代码中,我们使用了 restify 中的若干个常用插件:
- acceptParser 用来解析请求的 Accept 头,以确保是服务器端可以处理的类型。如果是服务器端不支持的类型,该插件会返回 406 错误。
- authorizationParser 用来解析请求中的 Authorization 头,并把解析的结果保存在请求对象的属性 authorization 中。
- queryParser 之前已经介绍过,用来解析请求中的查询字符串。
- gzipResponse 用来发送 GZIP 压缩之后的响应。
-
bodyParser 用来解析请求的内容,并把结果保存在请求对象的属性 body 中。目前支持的请求内容类型包括 application/json、application/x-www-form-urlencoded 和 multipart/form-data。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
const restify = require('restify'); const server = restify.createServer(); // 载入插件 server.use(restify.plugins.acceptParser(server.acceptable)); server.use(restify.plugins.authorizationParser()); server.use(restify.plugins.queryParser()); server.use(restify.plugins.gzipResponse()); server.use(restify.plugins.bodyParser()); server.post('/plugins', (req, res, next) => { console.log(req.body); res.send({a: 1}); return next(); }); server.listen(8000, () => console.log('%s listening at %s', server.name, server.url));
Reference
official document
使用 Restify 开发 REST API !