通读superagent文档[整理稿]

本文主要参考superagent的官方文档,基本上就是它的翻译。题外话,superagent真是一个不错的nodejs模块,推荐使用。

前言superagent是一个流行的nodejs第三方模块,专注于处理服务端/客户端的http请求。
在nodejs中,我们可以使用内置的http等模块来进行请求的发送、响应处理等操作,不过superagent提供了更加简单、优雅的API,让你在处理请求时更加方便。而且它很轻量,学习曲线平滑,内部其实就是对内置模块的封装。

下面让我们先来看一个简单的例子,大致的了解下request的基本用法,以及它和使用内置模块的区别。

var request = require('superagent');
var http = require('http');
var queryString = require('queryString');

使用superagent

request
    .post('/api/pet')
    .send({ name: 'Manny', species: 'cat' })
    .set('X-API-Key', 'foobar')
    .set('Accept', 'application/json')
    .end(function(err, res){
        if (res.ok) {
            alert('yay got ' + JSON.stringify(res.body));
        } else {
            alert('Oh no! error ' + res.text);
        }
    });

使用http

var postData = queryString.stringify({name: 'Manny', species: 'cat'});
var options = {
    path: '/api/pet',
    method: 'POST',
    headers: {
        'X-API-Key': 'foobar',
        'Accept': 'application/json'
    }
};
var req = http.request(options, function (res) {
    res.on('data', function (chunk) {
        console.log(chunk);
    });
});
req.on('error', function (err) {
    console.log(err);
});
req.write(postData);
req.end();

从上面的代码中,我们可以看出,使用superagent发送一个post请求,并设置了相关请求头信息(可以链式操作),相比较使用内置的模块,要简单很多。

基本用法在 var request = require('superagent') 之后, request 对象上将会有许多方法可供使用。我们可以使用 get 、 post 等方法名来声明一个get或者post请求,然后再通过 end() 方法来发出请求。

下面是个例子。

request
    .get('/search')
    .end(function (err, res) {

    })

这里有一点需要注意,就是只要当调用了 end() 方法之后,这个请求才会发出。在调用 end() 之前的所有动作其实都是对请求的配置。

我们在具体使用时,还可以将请求方法作为字符串参数,比如

request('GET', '/search').end(function (err, res) {});

在nodejs客户端中,还可以提供绝对路径,

request.get('http://www.baidu.com/search').end(function (err, res) {});

其余的http谓语动词也是可以使用,比如 DELETE 、 HEAD 、 POST 、 PUT 等等。

使用的时候,我们只需要变更 request[METHOD] 中的 METHOD 即可。

request.head('/favicon.ico').end(function (err, res) {});

不过,针对 DELETE 方法,有一点不同,因为 delete 是一个保留关键字,所以我在使用的时候应该是使用 del() 而不是 delete() 。

request.del('/user/111').end(function (err, res) {});

superagent默认的http方法是 GET 。就是说,如果你的请求是 get 请求,那么你可以省略http方法的相关配置。懒人必备。

request('/search', function(err, res) {});

设置请求头在之前的例子中,我们看到使用内置的http模块在设置请求头信息时,要求传递给 http.request 一个 options ,这个 options 包含了所有的请求头信息。相对这种方式,superagent提供了一种更佳简单优雅的方式。在superagent中,设置头信息,使用 set() 方法即可,而且它有多种形式,必能满足你的需求。

传递key-value键值对,可以一次设置一个头信息

request
    .get('/search')
    .set('X-API-Key', 'foobar')
    .set('Accept', 'application/json')
    .end(function (err, res) {

    });

传递对象,可以一次设置多次头信息

request
    .get('/search2')
    .set({'API-Key': 'foobar','Accept': 'application/json'})
    .end(function (err, res) {

    });

GET请求在使用 super.get 处理GET请求时,我们可以使用 query() 来处理请求字符串。比如,

request
    .get('/search')
    .query({name: 'Manny'})
    .query({range: '1..5'})
    .query({order: 'desc'})
    .end(function (err, res) {

    });

上面的代码将会发出一个 /search?name=Manny&range=1..5&order=desc 的请求。我们可以使用 query() 传递一个大对象,将所有的查询参数都写入。

request
    .get('/search')
    .query({name: 'Manny',range: '1..5',order: 'desc'})
    .end(function (err, res) {

    });

我们还可以简单粗暴的直接将整个查询字符串拼接起来作为 query() 的参数,

request
    .get('/search')
    .query('name=Manny&range=1..5&order=desc')
    .end(function (err, res) {

    });

或者是实时拼接字符串,

var name = 'Manny';
var range = '1..5';
var order = 'desc';
request
    .get('/search')
    .query('name=' + name)
    .query('range=' + range)
    .query('order=' + order)
    .end(function (err, res) {

    });

HEAD请求在 request.head 请求中,我们也可以使用 query() 来设置参数,

request
    .head('/users')
    .query({email: 'joe@gmail.com'})
    .end(function (err, res) {

    });

上面的代码将会发出一个 /users?email=joe@gamil.com 的请求。

POST/PUT请求一个典型的json post请求看起来像下面这样,

request
    .post('/user')
    .set('Content-Type', 'application/json')
    .send('{"name": "tj", "pet": "tobi"}')
    .end(callback);

我们通过 set() 设置一个合适的 Content-Type ,然后通过 send() 写入一些post data,最后通过 end() 发出这个post请求。注意这里我们 send() 的参数是一个json字符串。

因为json格式现在是一个通用的标准,所以默认的 Content-Type 就是json格式。下面的代码跟前一个示例是等价的,

request
    .post('/user')
    .send({name: "tj", pet: "tobi"})
    .end(callback);

我们也可以使用多个 send() 拼接post data,

request
    .post('/user')
    .send({name: "tj"})
    .send({pet: 'tobi'})
    .end(callback);

send() 方法发送字符串时,将默认设置 Content-Type 为 application/x-www-form-urlencoded ,多次 send() 的调用,将会使用 & 将所有的数据拼接起来。

比如下面这个例子,我们发送的数据将为 name=tj&per=tobi 。

request
    .post('/user')
    .send('name=tj')
    .send('pet=tobi')
    .end(callback);

superagent的请求数据格式化是可以扩展的,不过默认支持 form 和 json 两种格式,想发送数据以 application/x-www-form-urlencoded 类型的话,则可以简单的调用 type() 方法传递 form 参数就行, type() 方法的默认参数是 json 。

下面的代码将会使用 "name=tj&pet=tobi" 来发出一个post请求。

request
    .post('/user')
    .type('form')
    .send({name: 'tj'})
    .send({pet: 'tobi'})
    .end(callback);

设置Content-Type设置 Content-Type 最常用的方法就是使用 set() ,

request
    .post('/user')
    .set('Content-Type', 'application/json')
    .end(callback);

另一个简便的方法是调用 type() 方法,传递一个规范的 MIME 名称,包括 type/subtype ,或者是一个简单的后缀就像 xml , json , png 这样。

比如,

request
    .post('/user')
    .type('application/json')

request
    .post('/user')
    .type('json')

request
    .post('/user')
    .type('png')

设置Accept跟 type() 类似,设置accept也可以简单的调用 accept() 。参数接受一个规范的 MIME 名称,包括 type/subtype ,或者是一个简单的后缀就像 xml , json , png 这样。这个参数值将会被 request.types 所引用。

request
    .get('/user')
    .accept('application/json')

request
    .get('/user')
    .accept('json')

request
    .get('/user')
    .accept('png')

查询字符串当用 send(obj)方法来发送一个post请求,并且希望传递一些查询字符串时,可以调用 query() 方法,比如向 ?format=json&dest=/login 发送post请求,

request
    .post('/')
    .query({format: 'json'})
    .query({dest: '/login'})
    .send({post: 'data', here: 'wahoo'})
    .end(callback);

解析响应体superagent会解析一些常用的格式给请求者,当前支持 application/x-www-form-urlencoded , application/json , multipart/form-data 。
JSON/Urlencoded此种情况下, res.body 是一个解析过的对象。

比如一个请求的响应是一个json字符串 '{"user": {"name": "tobi"}}' ,那么 res.body.user.name 将会返回 tobi 。

同样的, x-www-form-urlencoded 格式的 user[name]=tobi 解析完的值是一样的。 Multipartnodejs客户端通过 Formidable 模块来支持 multipart/form-data 类型,当解析一个 multipart 响应时, res.files 属性就可以用了。假设一个请求响应下面的数据,

--whoop
Content-Disposition: attachment; name="image"; filename="tobi.png"
Content-Type: image/png
... data here ...
--whoop
Content-Disposition: form-data; name="name"
Content-Type: text/plain
Tobi
--whoop--

你将可以获取到 res.body.name 名为 'Tobi' , res.files.image 为一个 file 对象,包括一个磁盘文件路径,文件名称,还有其它的文件属性等。

响应体属性response 对象设置了许多有用的标识和属性。包括 response.text ,解析后的 respnse.body ,头信息,返回状态标识等。Response Textres.text 包含未解析前的响应内容。基于节省内存和性能因素的原因,一般只在mime类型能够匹配 text/ , json , x-www-form-urlencoding 的情况下默认为nodejs客户端提供。因为当响应以文件或者图片等大体积文件的情况下将会影响性能。

Response Body跟请求数据自动序列化一样,响应数据也会自动的解析。当 Content-Type 定义一个解析器后,就能自动解析,默认解析的Content-Type包含 application/json 和 application/x-www-form-urlencoded ,可以通过访问 res.body 来访问解析对象。

Response header fields res.header 包含解析之后的响应头数据,键值都是小写字母形式,比如 res.header['content-length'] 。 Response Content-TypeContent-Type 响应头字段是一个特列,服务器提供 res.type 来访问它,同时 res.charset 默认是空的。

比如,Content-Type值为 text/html; charset=utf8 ,则 res.type 为 text/html , res.charset 为 utf8 。

Response Status响应状态标识可以用来判断请求是否成功以及一些其他的额外信息。除此之外,还可以用superagent来构建理想的restful服务器。目前提供的标识定义如下,

var type = status / 100 | 0;
// status / class
res.status = status;
res.statusType = type;
// basicsres.info = 1 == type;
res.ok = 2 == type;
res.clientError = 4 == type;
res.serverError = 5 == type;
res.error = 4 == type || 5 == type;
// sugarres.accepted = 202 == status;
res.noContent = 204 == status || 1223 == status;
res.badRequest = 400 == status;
res.unauthorized = 401 == status;
res.notAcceptable = 406 == status;
res.notFound = 404 == status;res.forbidden = 403 == status;

中断请求要想中断请求,可以简单的调用 request.abort() 即可。

请求超时可以通过 request.timeout() 来定义超时时间。

当超时错误发生时,为了区别于别的错误, err.timeout 属性被定义为超时时间(一个 ms 的值)。注意,当超时错误发生后,后续的请求都会被重定向。意思就说超时是针对所有的请求而不是单个某个请求。

基本授权nodejs客户端目前提供两种基本授权的方式。

一种是通过类似下面这样的url,
request.get('http://tobi:learnboost@local').end(callback);

意思就是说要求在url中指明 user:password 。

第二种方式就是通过 auth() 方法。
request
    .get('http://local')
    .auth('tobi', 'learnboost')
    .end(callback);

跟随重定向默认是向上跟随5个重定向,不过可以通过调用 res.redirects(n) 来设置个数,

request
    .get('/some.png')
    .redirects(2)
    .end(callback);

管道数据nodejs客户端允许使用一个请求流来输送数据。

比如请求一个文件作为输出流,

var request = require('superagent');
var fs = require('fs');
var stream = fs.createReadStream('path/to/my.json');
var req = request.post('/somewhere');
req.type('json');
stream.pipe(req);

或者将一个响应流写到文件中,

var request = require('superagent');
var fs = require('fs');
var stream = fs.createWriteStream('path/to/my.json');
var req = request.get('/some.json');
req.pipe(stream);

复合请求superagent用来构建复合请求非常不错,提供了低级和高级的api方法。
低级的api是使用多个部分来表现一个文件或者字段, part() 方法返回一个新的部分,提供了跟request本身相似的api方法。

var req = request.post('/upload');
req.part()
    .set('Content-Type', 'image/png')
    .set('
        Content-Disposition', 'attachment; 
        filename="myimage.png"'
    )
    .write('some image data')
    .write('some more image data');
req.part()
    .set('Content-Disposition', 'form-data; name="name"')
    .set('Content-Type', 'text/plain')
    .write('tobi');
    req.end(callback);

附加文件 上面提及的高级api方法,可以通用 attach(name, [path], [filename]) 和 field(name, value) 这两种形式来调用。添加多个附件也比较简单,只需要给附件提供自定义的文件名称,同样的基础名称也要提供。

request
    .post('/upload')
    .attach('avatar', 'path/to/tobi.png', 'user.png')
    .attach('image', 'path/to/loki.png')
    .attach('file', 'path/to/jane.png')
    .end(callback);

字段值跟html的字段很像,你可以调用 field(name, value) 方法来设置字段。假设你想上传一个图片的时候带上自己的名称和邮箱,那么你可以像下面写的那样,

request
    .post('/upload')
    .field('user[name]', 'Tobi')
    .field('user[email]', 'tobi@learnboost.com')
    .attach('image', 'path/to/tobi.png')
    .end(callback);

压缩nodejs客户端本身对响应体就做了压缩,而且做的很好。所以这块你不需要做额外的事情了。

缓冲响应当想要强制缓冲 res.text 这样的响应内容时,可以调用 buffer() 方法。想取消默认的文本缓冲响应,比如 text/plain , text/html 这样的,可以调用 buffer(false) 方法。

一旦设置了缓冲标识 res.buffered ,那么就可以在一个回调函数里处理缓冲和没缓冲的响应。

跨域资源共享withCredentials() 方法可以激活发送原始cookie的能力。不过只有在 Access-Control-Allow-Origin 不是一个通配符( ),并且 *Access-Control-Allow-Credentials 为 true 的情况下才可以。

request
    .get('http://localhost:4001/')
    .withCredentials()
    .end(function(err, res, next){
        assert(200 == res.status);
        assert('tobi' == res.text);next();
    });

异常处理当请求出错时,superagent首先会检查回调函数的参数数量,当 err 参数提供的话,参数就是两个,如下

request
    .post('/upload')
    .attach('image', 'path/to/tobi.png')
    .end(function(err, res){

    });

如果没有回调函数,或者回调函数只有一个参数的话,可以添加error事件的处理。

request
    .post('/upload')
    .attach('image', 'path/to/tobi.png')
    .on('error', errorHandle)
    .end(function(res){

    });

注意:superagent 不认为 返回 4xx 和 5xx 的情况是错误。比如当请求返回500或者403之类的状态码时,可以通过 res.error 或者 res.status 等属性来查看。此时并不会有错误对象传递到回调函数中。当发生网络错误或者解析错误时,superagent才会认为是发生了请求错误,此时会传递一个错误对象 err 作为回调函数的第一个参数。

当产生一个4xx或者5xx的http响应, res.error 提供了一个错误信息的对象,你可以通过检查这个来做某些事情。

if (err && err.status === 404) {
    alert('oh no ' + res.body.message);
} else if (err) {
    // all other error types we handle generically
}

End! All rights reserved @gejiawen .

原文地址: 通读SuperAgent文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容