通讀SuperAgent文檔

2018-06-09 17:22 更新

本文主要參考superagent的官方文檔,基本上就是它的翻譯。

題外話,superagent真是一個(gè)不錯(cuò)的nodejs模塊,推薦使用。

前言

superagent是一個(gè)流行的nodejs第三方模塊,專注于處理服務(wù)端/客戶端的http請(qǐng)求。

在nodejs中,我們可以使用內(nèi)置的http等模塊來(lái)進(jìn)行請(qǐng)求的發(fā)送、響應(yīng)處理等操作,不過(guò)superagent提供了更加簡(jiǎn)單、優(yōu)雅的API,讓你在處理請(qǐng)求時(shí)更加方便。而且它很輕量,學(xué)習(xí)曲線平滑,內(nèi)部其實(shí)就是對(duì)內(nèi)置模塊的封裝。

下面讓我們先來(lái)看一個(gè)簡(jiǎn)單的例子,大致的了解下request的基本用法,以及它和使用內(nèi)置模塊的區(qū)別。


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發(fā)送一個(gè)post請(qǐng)求,并設(shè)置了相關(guān)請(qǐng)求頭信息(可以鏈?zhǔn)讲僮鳎?,相比較使用內(nèi)置的模塊,要簡(jiǎn)單很多。

基本用法

var request = require('superagent')之后,request對(duì)象上將會(huì)有許多方法可供使用。我們可以使用get、post等方法名來(lái)聲明一個(gè)get或者post請(qǐng)求,然后再通過(guò)end()方法來(lái)發(fā)出請(qǐng)求。下面是個(gè)例子。


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

這里有一點(diǎn)需要注意,就是只要當(dāng)調(diào)用了end()方法之后,這個(gè)請(qǐng)求才會(huì)發(fā)出。在調(diào)用end()之前的所有動(dòng)作其實(shí)都是對(duì)請(qǐng)求的配置。

我們?cè)诰唧w使用時(shí),還可以將請(qǐng)求方法作為字符串參數(shù),比如


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

在nodejs客戶端中,還可以提供絕對(duì)路徑,


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

其余的http謂語(yǔ)動(dòng)詞也是可以使用,比如DELETE、HEAD、POSTPUT等等。使用的時(shí)候,我們只需要變更request[METHOD]中的METHOD即可。


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

不過(guò),針對(duì)DELETE方法,有一點(diǎn)不同,因?yàn)?code>delete是一個(gè)保留關(guān)鍵字,所以我在使用的時(shí)候應(yīng)該是使用del()而不是delete()


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

superagent默認(rèn)的http方法是GET。就是說(shuō),如果你的請(qǐng)求是get請(qǐng)求,那么你可以省略http方法的相關(guān)配置。懶人必備。


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

設(shè)置請(qǐng)求頭

在之前的例子中,我們看到使用內(nèi)置的http模塊在設(shè)置請(qǐng)求頭信息時(shí),要求傳遞給http.request一個(gè)options,這個(gè)options包含了所有的請(qǐng)求頭信息。相對(duì)這種方式,superagent提供了一種更佳簡(jiǎn)單優(yōu)雅的方式。在superagent中,設(shè)置頭信息,使用set()方法即可,而且它有多種形式,必能滿足你的需求。


// 傳遞key-value鍵值對(duì),可以一次設(shè)置一個(gè)頭信息
request
    .get('/search')
    .set('X-API-Key', 'foobar')
    .set('Accept', 'application/json')
    .end(function (err, res) {
    });
// 傳遞對(duì)象,可以一次設(shè)置多次頭信息
request
    .get('/search2')
    .set({
        'API-Key': 'foobar',
        'Accept': 'application/json'
    })
    .end(function (err, res) {
    });

GET請(qǐng)求

在使用super.get處理GET請(qǐng)求時(shí),我們可以使用query()來(lái)處理請(qǐng)求字符串。比如,


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

上面的代碼將會(huì)發(fā)出一個(gè)/search?name=Manny&range=1..5&order=desc的請(qǐng)求。

我們可以使用query()傳遞一個(gè)大對(duì)象,將所有的查詢參數(shù)都寫入。


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

我們還可以簡(jiǎn)單粗暴的直接將整個(gè)查詢字符串拼接起來(lái)作為query()的參數(shù),


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

或者是實(shí)時(shí)拼接字符串,


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請(qǐng)求

request.head請(qǐng)求中,我們也可以使用query()來(lái)設(shè)置參數(shù),


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

上面的代碼將會(huì)發(fā)出一個(gè)/users?email=joe@gamil.com的請(qǐng)求。

POST/PUT請(qǐng)求

一個(gè)典型的json post請(qǐng)求看起來(lái)像下面這樣,


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

我們通過(guò)set()設(shè)置一個(gè)合適的Content-Type,然后通過(guò)send()寫入一些post data,最后通過(guò)end()發(fā)出這個(gè)post請(qǐng)求。注意這里我們send()的參數(shù)是一個(gè)json字符串。

因?yàn)閖son格式現(xiàn)在是一個(gè)通用的標(biāo)準(zhǔn),所以默認(rèn)的Content-Type就是json格式。下面的代碼跟前一個(gè)示例是等價(jià)的,


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

我們也可以使用多個(gè)send()拼接post data,


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

send()方法發(fā)送字符串時(shí),將默認(rèn)設(shè)置Content-Typeapplication/x-www-form-urlencoded,多次send()的調(diào)用,將會(huì)使用&將所有的數(shù)據(jù)拼接起來(lái)。比如下面這個(gè)例子,我們發(fā)送的數(shù)據(jù)將為name=tj&per=tobi


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

superagent的請(qǐng)求數(shù)據(jù)格式化是可以擴(kuò)展的,不過(guò)默認(rèn)支持formjson兩種格式,想發(fā)送數(shù)據(jù)以application/x-www-form-urlencoded類型的話,則可以簡(jiǎn)單的調(diào)用type()方法傳遞form參數(shù)就行,type()方法的默認(rèn)參數(shù)是json。

下面的代碼將會(huì)使用"name=tj&pet=tobi"來(lái)發(fā)出一個(gè)post請(qǐng)求。


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

設(shè)置Content-Type

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


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

另一個(gè)簡(jiǎn)便的方法是調(diào)用type()方法,傳遞一個(gè)規(guī)范的MIME名稱,包括type/subtype,或者是一個(gè)簡(jiǎn)單的后綴就像xml,json,png這樣。比如,


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

設(shè)置Accept

type()類似,設(shè)置accept也可以簡(jiǎn)單的調(diào)用accept()。參數(shù)接受一個(gè)規(guī)范的MIME名稱,包括type/subtype,或者是一個(gè)簡(jiǎn)單的后綴就像xml,json,png這樣。這個(gè)參數(shù)值將會(huì)被request.types所引用。


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

查詢字符串

當(dāng)用send(obj)方法來(lái)發(fā)送一個(gè)post請(qǐng)求,并且希望傳遞一些查詢字符串時(shí),可以調(diào)用query()方法,比如向?format=json&dest=/login發(fā)送post請(qǐng)求,


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

解析響應(yīng)體

superagent會(huì)解析一些常用的格式給請(qǐng)求者,當(dāng)前支持application/x-www-form-urlencoded,application/json,multipart/form-data

JSON/Urlencoded

此種情況下,res.body是一個(gè)解析過(guò)的對(duì)象。比如一個(gè)請(qǐng)求的響應(yīng)是一個(gè)json字符串'{"user": {"name": "tobi"}}',那么res.body.user.name將會(huì)返回tobi。

同樣的,x-www-form-urlencoded格式的user[name]=tobi解析完的值是一樣的。

Multipart

nodejs客戶端通過(guò)Formidable模塊來(lái)支持multipart/form-data類型,當(dāng)解析一個(gè)multipart響應(yīng)時(shí),res.files屬性就可以用了。假設(shè)一個(gè)請(qǐng)求響應(yīng)下面的數(shù)據(jù),


--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為一個(gè)file對(duì)象,包括一個(gè)磁盤文件路徑,文件名稱,還有其它的文件屬性等。

響應(yīng)體屬性

response對(duì)象設(shè)置了許多有用的標(biāo)識(shí)和屬性。包括response.text,解析后的respnse.body,頭信息,返回狀態(tài)標(biāo)識(shí)等。

Response Text

res.text包含未解析前的響應(yīng)內(nèi)容?;诠?jié)省內(nèi)存和性能因素的原因,一般只在mime類型能夠匹配text/json,x-www-form-urlencoding的情況下默認(rèn)為nodejs客戶端提供。因?yàn)楫?dāng)響應(yīng)以文件或者圖片等大體積文件的情況下將會(huì)影響性能。

Response Body

跟請(qǐng)求數(shù)據(jù)自動(dòng)序列化一樣,響應(yīng)數(shù)據(jù)也會(huì)自動(dòng)的解析。當(dāng)Content-Type定義一個(gè)解析器后,就能自動(dòng)解析,默認(rèn)解析的Content-Type包含application/jsonapplication/x-www-form-urlencoded,可以通過(guò)訪問(wèn)res.body來(lái)訪問(wèn)解析對(duì)象。

Response header fields

res.header包含解析之后的響應(yīng)頭數(shù)據(jù),鍵值都是小寫字母形式,比如res.header['content-length']。

Response Content-Type

Content-Type響應(yīng)頭字段是一個(gè)特列,服務(wù)器提供res.type來(lái)訪問(wèn)它,同時(shí)res.charset默認(rèn)是空的。比如,Content-Type值為text/html; charset=utf8,則res.typetext/html,res.charsetutf8

Response Status

響應(yīng)狀態(tài)標(biāo)識(shí)可以用來(lái)判斷請(qǐng)求是否成功以及一些其他的額外信息。除此之外,還可以用superagent來(lái)構(gòu)建理想的restful服務(wù)器。目前提供的標(biāo)識(shí)定義如下,


var type = status / 100 | 0;
// status / class
res.status = status;
res.statusType = type;
// basics
res.info = 1 == type;
res.ok = 2 == type;
res.clientError = 4 == type;
res.serverError = 5 == type;
res.error = 4 == type || 5 == type;
// sugar
res.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;

中斷請(qǐng)求

要想中斷請(qǐng)求,可以簡(jiǎn)單的調(diào)用request.abort()即可。

請(qǐng)求超時(shí)

可以通過(guò)request.timeout()來(lái)定義超時(shí)時(shí)間。當(dāng)超時(shí)錯(cuò)誤發(fā)生時(shí),為了區(qū)別于別的錯(cuò)誤,err.timeout屬性被定義為超時(shí)時(shí)間(一個(gè)ms的值)。注意,當(dāng)超時(shí)錯(cuò)誤發(fā)生后,后續(xù)的請(qǐng)求都會(huì)被重定向。意思就說(shuō)超時(shí)是針對(duì)所有的請(qǐng)求而不是單個(gè)某個(gè)請(qǐng)求。

基本授權(quán)

nodejs客戶端目前提供兩種基本授權(quán)的方式。

一種是通過(guò)類似下面這樣的url,


request.get('http://tobi:learnboost@local').end(callback);

意思就是說(shuō)要求在url中指明user:password。

第二種方式就是通過(guò)auth()方法。


request
    .get('http://local')
    .auth('tobi', 'learnboost')
    .end(callback);

跟隨重定向

默認(rèn)是向上跟隨5個(gè)重定向,不過(guò)可以通過(guò)調(diào)用res.redirects(n)來(lái)設(shè)置個(gè)數(shù),


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

管道數(shù)據(jù)

nodejs客戶端允許使用一個(gè)請(qǐng)求流來(lái)輸送數(shù)據(jù)。比如請(qǐng)求一個(gè)文件作為輸出流,


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);

或者將一個(gè)響應(yīng)流寫到文件中,


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);

復(fù)合請(qǐng)求

superagent用來(lái)構(gòu)建復(fù)合請(qǐng)求非常不錯(cuò),提供了低級(jí)和高級(jí)的api方法。

低級(jí)的api是使用多個(gè)部分來(lái)表現(xiàn)一個(gè)文件或者字段,part()方法返回一個(gè)新的部分,提供了跟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);

附加文件

上面提及的高級(jí)api方法,可以通用attach(name, [path], [filename])field(name, value)這兩種形式來(lái)調(diào)用。添加多個(gè)附件也比較簡(jiǎn)單,只需要給附件提供自定義的文件名稱,同樣的基礎(chǔ)名稱也要提供。

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的字段很像,你可以調(diào)用field(name, value)方法來(lái)設(shè)置字段。假設(shè)你想上傳一個(gè)圖片的時(shí)候帶上自己的名稱和郵箱,那么你可以像下面寫的那樣,

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

壓縮

nodejs客戶端本身對(duì)響應(yīng)體就做了壓縮,而且做的很好。所以這塊你不需要做額外的事情了。

緩沖響應(yīng)

當(dāng)想要強(qiáng)制緩沖res.text這樣的響應(yīng)內(nèi)容時(shí),可以調(diào)用buffer()方法。想取消默認(rèn)的文本緩沖響應(yīng),比如text/plaintext/html這樣的,可以調(diào)用buffer(false)方法。

一旦設(shè)置了緩沖標(biāo)識(shí)res.buffered,那么就可以在一個(gè)回調(diào)函數(shù)里處理緩沖和沒(méi)緩沖的響應(yīng)。

跨域資源共享

withCredentials()方法可以激活發(fā)送原始cookie的能力。不過(guò)只有在Access-Control-Allow-Origin不是一個(gè)通配符(),并且*Access-Control-Allow-Credentialstrue的情況下才可以。


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

異常處理

當(dāng)請(qǐng)求出錯(cuò)時(shí),superagent首先會(huì)檢查回調(diào)函數(shù)的參數(shù)數(shù)量,當(dāng)err參數(shù)提供的話,參數(shù)就是兩個(gè),如下


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

如果沒(méi)有回調(diào)函數(shù),或者回調(diào)函數(shù)只有一個(gè)參數(shù)的話,可以添加error事件的處理。


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

注意:superagent不認(rèn)為返回4xx5xx的情況是錯(cuò)誤。比如當(dāng)請(qǐng)求返回500或者403之類的狀態(tài)碼時(shí),可以通過(guò)res.error或者res.status等屬性來(lái)查看。此時(shí)并不會(huì)有錯(cuò)誤對(duì)象傳遞到回調(diào)函數(shù)中。當(dāng)發(fā)生網(wǎng)絡(luò)錯(cuò)誤或者解析錯(cuò)誤時(shí),superagent才會(huì)認(rèn)為是發(fā)生了請(qǐng)求錯(cuò)誤,此時(shí)會(huì)傳遞一個(gè)錯(cuò)誤對(duì)象err作為回調(diào)函數(shù)的第一個(gè)參數(shù)。

當(dāng)產(chǎn)生一個(gè)4xx或者5xx的http響應(yīng),res.error提供了一個(gè)錯(cuò)誤信息的對(duì)象,你可以通過(guò)檢查這個(gè)來(lái)做某些事情。


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


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)