客户端

事实上在 restify 中有三个独立的客户端:

  • JsonClient: 发送并期望是 application/json
  • StringClient: 发送网址编码的请求并期望是 text/plain
  • HttpClient: 封装了 Node.js 的 http/https 库

这个想法是,如果您想支持“典型” control-plane REST API,您可能需要 JsonClient,或者如果您使用其他序列化模式(比如 XML),您会编写自己的客户端来扩展 StringClient。如果您需要支持流,您需要在 HttpClient 之上做一些工作,因为 StringClient 对缓冲请求/响应并不友好。

所有客户端都支持以指数回退重试以获取 TCP 连接;他们不会像之前版本的 restify 客户端那样对 5xx 错误码执行重试。您可以将 retry 设置为 false,以完全禁用此逻辑。此外,所有客户端都支持 connectTimeout 字段,每次重试都使用该字段。默认情况下不设置 connectTimeout,所以您最终得到了 Node.js 套接字的默认值。

这是一个连接 Joyent CloudAPI 的例子:

  1. var restify = require('restify-clients');
  2. // 创建一个 JSON 客户端
  3. var client = restify.createJsonClient({
  4. url: 'https://us-east-1.api.joyent.com'
  5. });
  6. client.basicAuth('$login', '$password');
  7. client.get('/my/machines', function(err, req, res, obj) {
  8. assert.ifError(err);
  9. console.log(JSON.stringify(obj, null, 2));
  10. });

按照简洁模式,客户端可以使用字符串 URL 而不是选项对象进行初始化:

  1. var restify = require('restify-clients');
  2. var client = restify.createJsonClient('https://us-east-1.api.joyent.com');

请注意,所有的进阶文档都提到了像 get/put/del 这样的采用字符串路径的“简洁”形式。 您还可以将一个对象传递给具有额外参数(特别是报头)的任何方法:

  1. var options = {
  2. path: '/foo/bar',
  3. headers: {
  4. 'x-foo': 'bar'
  5. },
  6. retry: {
  7. 'retries': 0
  8. },
  9. agent: false
  10. };
  11. client.get(options, function(err, req, res) { .. });

如果在将请求发送到服务器之前您需要在请求中插入其他报头,您可以在创建客户端时提供同步回调函数作为 signRequest 选项。这对 node-http-signature 特别有用,因为它需要附加所选传出报头的加密签名。如果提供,将使用单个参数调用此回调函数:传出的 http.ClientRequest 对象。

JsonClient

JSON 客户端是与 restify-clients 绑定在一起的最高级客户端;它会导出一组直接映射到 HTTP 动词的方法。所有的回调看起来像函数 function(err, req, res, [obj]),其中 obj 是可选的,取决于是否返回内容。HTTP 状态码不被解释,所以如果服务器返回 4xx 或者带有 JSON 负载的内容,obj 将会是该 JSON 负载。但是,如果服务器返回状态码大于等于 400(这将是其中一个 HTTP 错误),则会设置 err。如果 err 看起来像 RestError

  1. {
  2. "code": "FooError",
  3. "message": "some foo happened"
  4. }

那么 err 会为您 “upconverted” 为 RestError。否则会是一个 HttpError

createJsonClient(options)

  1. var client = restify.createJsonClient({
  2. url: 'https://api.us-east-1.joyent.com',
  3. version: '*'
  4. });

选项:

名称 类型 描述
accept String 发送可接受的报头
connectTimeout Number 套接字的超时时间
requestTimeout Number 请求完成的超时时间
dtrace Object node-dtrace-provider 句柄
gzip Object 将在发送时使用 content-encoding: gzip 压缩数据
headers Object 在所有请求中设置 HTTP 报头
log Object bunyan 实例
retry Object 为 node-retry 选项提供;”false” 禁用重试;默认重试 4 次
signRequest Function 在发送请求之前插入报头的同步回调
url String 要连接的完整 URL
userAgent String 使用 user-agent 字符串;restify 会插入一个,但您可以覆盖它
version String semver 字符串来设置可接受版本

get(path, callback)

执行 HTTP get;如果没有有效负载被返回,obj 默认为 {}(因此您不会得到一堆空指针错误)。

  1. client.get('/foo/bar', function(err, req, res, obj) {
  2. assert.ifError(err);
  3. console.log('%j', obj);
  4. });

head(path, callback)

get 一样,但没有 obj

  1. client.head('/foo/bar', function(err, req, res) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. });

post(path, object, callback)

需要将一个完整的对象序列化并发送到服务器。

  1. client.post('/foo', { hello: 'world' }, function(err, req, res, obj) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. console.log('%j', obj);
  5. });

put(path, object, callback)

post 一样:

  1. client.put('/foo', { hello: 'world' }, function(err, req, res, obj) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. console.log('%j', obj);
  5. });

del(path, callback)

del 不需要内容,正如您所知的,它不应该有内容:

  1. client.del('/foo/bar', function(err, req, res) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. });

StringClient

StringClient 是构建 JsonClient 的基础,并为您提供了编写其他缓冲/解析客户端(如称 XML 客户端)的基础。如果您需要与某些“原始” HTTP 服务器交互,那么 StringClient 就是您想要的,因为默认情况下,它会为您的内容上传提供 application/x-www-form-url-encoded,并以 text/plain 格式下载。要扩展一个 StringClient,请查看 JsonClient 的源代码。实际上,您扩展它,需要在构造函数中设置适当的选项,并实现 write(针对 put/post)和 parse 方法(针对所有 HTTP 主体),这样就可以了。

createStringClient(options)

  1. var client = restify.createStringClient({
  2. url: 'https://example.com'
  3. })

get(path, callback)

执行 HTTP get;如果没有有效载荷被返回,那么 data 默认为 ''(因此您不会得到一堆空指针错误)。

  1. client.get('/foo/bar', function(err, req, res, data) {
  2. assert.ifError(err);
  3. console.log('%s', data);
  4. });

head(path, callback)

get 一样,但没有 data

  1. client.head('/foo/bar', function(err, req, res) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. });

post(path, object, callback)

需要将一个完整的对象序列化并发送到服务器。

  1. client.post('/foo', { hello: 'world' }, function(err, req, res, data) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. console.log('%s', data);
  5. });

put(path, object, callback)

post 一样:

  1. client.put('/foo', { hello: 'world' }, function(err, req, res, data) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. console.log('%s', data);
  5. });

del(path, callback)

del 不需要内容,正如您所知的,它不应该有内容:

  1. client.del('/foo/bar', function(err, req, res) {
  2. assert.ifError(err);
  3. console.log('%d -> %j', res.statusCode, res.headers);
  4. });

HttpClient

HttpClient 是 restify 中发布的最低级别的客户端,基本上只是 Node.js 的 http/https 模块顶部的一些语法糖(与其他客户端一样的 HTTP 方法)。如果你想用 restify 进行流处理,这将非常有用。请注意,以下事件很不幸地被命名为 result 而不是 response(因为事件 ‘response’ 已被占用)。

  1. client = restify.createClient({
  2. url: 'http://127.0.0.1'
  3. });
  4. client.get('/str/mcavage', function(err, req) {
  5. assert.ifError(err); // 连接错误
  6. req.on('result', function(err, res) {
  7. assert.ifError(err); // HTTP 状态码 >= 400
  8. res.body = '';
  9. res.setEncoding('utf8');
  10. res.on('data', function(chunk) {
  11. res.body += chunk;
  12. });
  13. res.on('end', function() {
  14. console.log(res.body);
  15. });
  16. });
  17. });

或写一个:

  1. client.post(opts, function(err, req) {
  2. assert.ifError(connectErr);
  3. req.on('result', function(err, res) {
  4. assert.ifError(err);
  5. res.body = '';
  6. res.setEncoding('utf8');
  7. res.on('data', function(chunk) {
  8. res.body += chunk;
  9. });
  10. res.on('end', function() {
  11. console.log(res.body);
  12. });
  13. });
  14. req.write('hello world');
  15. req.end();
  16. });

请注意 get/head/del 都会为您调用 req.end(),所以您不能在这些方法上写数据。否则,会在所有与 JsonClient/StringClient 同名的方法中存在。

希望扩展 HttpClient 的人应该看看源码,注意可能需要覆写 readwrite

代理

有几个选项用于启用 http 客户端代理。以下选项可用于设置代理网址:

  1. // 在客户端配置中设置代理选项
  2. restify.createClient({
  3. proxy: 'http://127.0.0.1'
  4. });

来自环境变量:

  1. $ export HTTPS_PROXY = 'https://127.0.0.1'
  2. $ export HTTP_PROXY = 'http://127.0.0.1'

有一个选项可以禁止在单个网址或所有网址上使用代理服务器。这可以通过设置环境变量来启用。

不代理任何网址的请求

  1. $ export NO_PROXY='*'

不代理本地网址的请求

  1. $ export NO_PROXY='127.0.0.1'

不代理本地网址其中端口为 8000 的请求

  1. $ export NO_PROXY='localhost:8000'

不代理多个 IP 地址的请求

  1. $ export NO_PROXY='127.0.0.1, 8.8.8.8'

注意:所请求的 url 必须与代理配置中的完整主机名或 NO_PROXY 环境变量相匹配。不执行 DNS 查找来确定主机名的 IP 地址。

basicAuth(username, password)

此快捷方法(适用于所有客户端),只需为所有 HTTP 请求设置 Authorization 标头就可以了:

  1. client.basicAuth('mark', 'mysupersecretpassword');

升级

如果您成功与 HTTP 服务器协商升级,会发出一个带有参数 errressocketheadupgradeResult 事件。您可以使用此功能与服务器建立 WebSockets 连接。例如,使用 watershed 库:

  1. var ws = new Watershed();
  2. var wskey = ws.generateKey();
  3. var options = {
  4. path: '/websockets/attach',
  5. headers: {
  6. connection: 'upgrade',
  7. upgrade: 'websocket',
  8. 'sec-websocket-key': wskey,
  9. }
  10. };
  11. client.get(options, function(err, res, socket, head) {
  12. res.once('upgradeResult', function(err2, res2, socket2, head2) {
  13. var shed = ws.connect(res2, socket2, head2, wskey);
  14. shed.on('text', function(msg) {
  15. console.log('message from server: ' + msg);
  16. shed.end();
  17. });
  18. shed.send('greetings program');
  19. });
  20. });