本文主要參考superagent的官方文檔,基本上就是它的翻譯。
題外話,superagent真是一個不錯的nodejs模塊,推薦使用。
superagent是一個流行的nodejs第三方模塊,專注于處理服務端/客戶端的http請求。
在nodejs中,我們可以使用內置的http等模塊來進行請求的發(fā)送、響應處理等操作,不過superagent提供了更加簡單、優(yōu)雅的API,讓你在處理請求時更加方便。而且它很輕量,學習曲線平滑,內部其實就是對內置模塊的封裝。
下面讓我們先來看一個簡單的例子,大致的了解下request的基本用法,以及它和使用內置模塊的區(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ā)送一個post請求,并設置了相關請求頭信息(可以鏈式操作),相比較使用內置的模塊,要簡單很多。
在var request = require('superagent')
之后,request
對象上將會有許多方法可供使用。我們可以使用get
、post
等方法名來聲明一個get或者post請求,然后再通過end()
方法來發(fā)出請求。下面是個例子。
request
.get('/search')
.end(function (err, res) {
})
這里有一點需要注意,就是只要當調用了end()
方法之后,這個請求才會發(fā)出。在調用end()
之前的所有動作其實都是對請求的配置。
我們在具體使用時,還可以將請求方法作為字符串參數(shù),比如
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提供了一種更佳簡單優(yōu)雅的方式。在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) {
});
在使用super.get
處理GET請求時,我們可以使用query()
來處理請求字符串。比如,
request
.get('/search')
.query({name: 'Manny'})
.query({range: '1..5'})
.query({order: 'desc'})
.end(function (err, res) {
});
上面的代碼將會發(fā)出一個/search?name=Manny&range=1..5&order=desc
的請求。
我們可以使用query()
傳遞一個大對象,將所有的查詢參數(shù)都寫入。
request
.get('/search')
.query({
name: 'Manny',
range: '1..5',
order: 'desc'
})
.end(function (err, res) {
});
我們還可以簡單粗暴的直接將整個查詢字符串拼接起來作為query()
的參數(shù),
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) {
});
在request.head
請求中,我們也可以使用query()
來設置參數(shù),
request
.head('/users')
.query({
email: 'joe@gmail.com'
})
.end(function (err, res) {
});
上面的代碼將會發(fā)出一個/users?email=joe@gamil.com
的請求。
一個典型的json post請求看起來像下面這樣,
request
.post('/user')
.set('Content-Type', 'application/json')
.send('{"name": "tj", "pet": "tobi"}')
.end(callback);
我們通過set()
設置一個合適的Content-Type
,然后通過send()
寫入一些post data,最后通過end()
發(fā)出這個post請求。注意這里我們send()
的參數(shù)是一個json字符串。
因為json格式現(xiàn)在是一個通用的標準,所以默認的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()
方法發(fā)送字符串時,將默認設置Content-Type
為application/x-www-form-urlencoded
,多次send()
的調用,將會使用&
將所有的數(shù)據(jù)拼接起來。比如下面這個例子,我們發(fā)送的數(shù)據(jù)將為name=tj&per=tobi
。
request
.post('/user')
.send('name=tj')
.send('pet=tobi')
.end(callback);
superagent的請求數(shù)據(jù)格式化是可以擴展的,不過默認支持form
和json
兩種格式,想發(fā)送數(shù)據(jù)以application/x-www-form-urlencoded
類型的話,則可以簡單的調用type()
方法傳遞form
參數(shù)就行,type()
方法的默認參數(shù)是json
。
下面的代碼將會使用"name=tj&pet=tobi"
來發(fā)出一個post請求。
request
.post('/user')
.type('form')
.send({name: 'tj'})
.send({pet: 'tobi'})
end(callback);
設置Content-Type
最常用的方法就是使用set()
,
request
.post('/user')
.set('Content-Type', 'application/json')
.end(callback);
另一個簡便的方法是調用type()
方法,傳遞一個規(guī)范的MIME
名稱,包括type/subtype
,或者是一個簡單的后綴就像xml
,json
,png
這樣。比如,
request.post('/user').type('application/json')
request.post('/user').type('json')
request.post('/user').type('png')
跟type()
類似,設置accept也可以簡單的調用accept()
。參數(shù)接受一個規(guī)范的MIME
名稱,包括type/subtype
,或者是一個簡單的后綴就像xml
,json
,png
這樣。這個參數(shù)值將會被request.types
所引用。
request.get('/user').accept('application/json')
request.get('/user').accept('json')
request.get('/user').accept('png')
當用send(obj)
方法來發(fā)送一個post請求,并且希望傳遞一些查詢字符串時,可以調用query()
方法,比如向?format=json&dest=/login
發(fā)送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
。
此種情況下,res.body
是一個解析過的對象。比如一個請求的響應是一個json字符串'{"user": {"name": "tobi"}}'
,那么res.body.user.name
將會返回tobi
。
同樣的,x-www-form-urlencoded
格式的user[name]=tobi
解析完的值是一樣的。
nodejs客戶端通過Formidable模塊來支持multipart/form-data
類型,當解析一個multipart
響應時,res.files
屬性就可以用了。假設一個請求響應下面的數(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
為一個file對象,包括一個磁盤文件路徑,文件名稱,還有其它的文件屬性等。
response
對象設置了許多有用的標識和屬性。包括response.text
,解析后的respnse.body
,頭信息,返回狀態(tài)標識等。
res.text
包含未解析前的響應內容?;诠?jié)省內存和性能因素的原因,一般只在mime類型能夠匹配text/
,json
,x-www-form-urlencoding
的情況下默認為nodejs客戶端提供。因為當響應以文件或者圖片等大體積文件的情況下將會影響性能。
跟請求數(shù)據(jù)自動序列化一樣,響應數(shù)據(jù)也會自動的解析。當Content-Type
定義一個解析器后,就能自動解析,默認解析的Content-Type包含application/json
和application/x-www-form-urlencoded
,可以通過訪問res.body
來訪問解析對象。
res.header
包含解析之后的響應頭數(shù)據(jù),鍵值都是小寫字母形式,比如res.header['content-length']
。
Content-Type
響應頭字段是一個特列,服務器提供res.type
來訪問它,同時res.charset
默認是空的。比如,Content-Type值為text/html; charset=utf8
,則res.type
為text/html
,res.charset
為utf8
。
響應狀態(tài)標識可以用來判斷請求是否成功以及一些其他的額外信息。除此之外,還可以用superagent來構建理想的restful服務器。目前提供的標識定義如下,
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;
要想中斷請求,可以簡單的調用request.abort()
即可。
可以通過request.timeout()
來定義超時時間。當超時錯誤發(fā)生時,為了區(qū)別于別的錯誤,err.timeout
屬性被定義為超時時間(一個ms
的值)。注意,當超時錯誤發(fā)生后,后續(xù)的請求都會被重定向。意思就說超時是針對所有的請求而不是單個某個請求。
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)
來設置個數(shù),
request
.get('/some.png')
.redirects(2)
.end(callback);
nodejs客戶端允許使用一個請求流來輸送數(shù)據(jù)。比如請求一個文件作為輸出流,
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是使用多個部分來表現(xiàn)一個文件或者字段,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
,那么就可以在一個回調函數(shù)里處理緩沖和沒緩沖的響應。
withCredentials()
方法可以激活發(fā)送原始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首先會檢查回調函數(shù)的參數(shù)數(shù)量,當err參數(shù)提供的話,參數(shù)就是兩個,如下
request
.post('/upload')
.attach('image', 'path/to/tobi.png')
.end(function(err, res){
});
如果沒有回調函數(shù),或者回調函數(shù)只有一個參數(shù)的話,可以添加error事件的處理。
request
.post('/upload')
.attach('image', 'path/to/tobi.png')
.on('error', errorHandle)
.end(function(res){
});
注意:superagent不認為返回4xx和5xx的情況是錯誤。比如當請求返回500或者403之類的狀態(tài)碼時,可以通過res.error
或者res.status
等屬性來查看。此時并不會有錯誤對象傳遞到回調函數(shù)中。當發(fā)生網(wǎng)絡錯誤或者解析錯誤時,superagent才會認為是發(fā)生了請求錯誤,此時會傳遞一個錯誤對象err
作為回調函數(shù)的第一個參數(shù)。
當產(chǎn)生一個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
}
更多建議: