nodejs http模組的講解以及request包的使用
http
大部分的node
使用者,都是用node
來做Web API的,而HTTP模組是提供Web API的基礎。為了支援所有的HTTP應用,node
中的HTTTP模組提供的API是偏向底層化的。利用HTTP模組,我們可以簡單快速搭建一個Web Server。
node
提供了http這個核心模組(不用安裝哦,直接require就可以了),用於建立http server
服務,使用下面程式碼,輕鬆在本機的3000埠建立一個http伺服器
// http_demo.js
var http=require("http");
http.createServer(function(req,res){
res.writeHead(200 ,{
"content-type":"text/plain"
});
res.write("hello world");
res.end();
}).listen(3000);
$~ node http_demo.js
流程
我們首先用http.createServer函式建立了一個伺服器物件,然後呼叫了response.writeHead方法:該方法的第一個引數表示HTTP的響應狀態(200)表示一切正常;第二個引數是“Content-Type”,表示我響應給客戶端的內容型別。然再後我們呼叫了response.write方法,寫入我們需要傳遞給客戶端的內容。最後一步我們呼叫了response.end,表示此次請求已處理完成。
.listen(port)
此函式有兩個引數,第一個引數表示我們需要監聽的埠,第二個引數是回撥函式(其實是listening事件),當監聽開啟後立刻觸發。
下面我們逐步展開HTTP 的 API
httpService (http伺服器)
開篇的例項程式碼,也可以通過如下的程式碼進行改寫一番:
var http=require("http");
var server=new http.Server();
server.on("request",function(req,res){
res.writeHead(200,{
"content-type":"text/plain"
});
res.write("hello nodejs" );
res.end();
});
server.listen(3000);
以上程式碼是通過直接建立一個http.Server
物件,然後為其新增request
事件監聽,其實也就說createServer
方法其實本質上也是為http.Server
物件添加了一個request
事件監聽,這似乎更好理解了,那讓我們看看http的重要屬性
createServer
方法中的引數函式中的兩個引數req和res則是分別代表了請求物件和響應物件。其中req是http.IncomingMessage
的例項,res是http.ServerResponse
的例項。
http.IncomingMessage
http.IncomingMessage是HTTP請求的資訊,是後端開發者最關注的內容,一般由http.Server的request事件傳送,並作為第一個引數傳遞,包含三個事件
- data:當請求體資料到來時,該事件被觸發,該事件提供一個引數chunk,表示接受的資料,如果該事件沒有被監聽,則請求體會被拋棄,該事件可能會被呼叫多次(這與nodejs是非同步的有關係)
- end:當請求體資料傳輸完畢時,該事件會被觸發,此後不會再有資料
- close:使用者當前請求結束時,該事件被觸發,不同於end,如果使用者強制終止了傳輸,也是用close
http.ServerResponse
http.ServerResponse是返回給客戶端的資訊,決定了使用者最終看到的內容,一般也由http.Server的request事件傳送,並作為第二個引數傳遞,它有三個重要的成員函式,用於返回響應頭、響應內容以及結束請求
- res.writeHead(statusCode,[heasers]):向請求的客戶端傳送響應頭,該函式在一個請求中最多呼叫一次,如果不呼叫,則會自動生成一個響應頭
- res.write(data,[encoding]):想請求的客戶端傳送相應內容,data是一個buffer或者字串,如果data是字串,則需要制定編碼方式,預設為utf-8,在res.end呼叫之前可以多次呼叫
- res.end([data],[encoding]):結束響應,告知客戶端所有傳送已經結束,當所有要返回的內容傳送完畢時,該函式必需被呼叫一次,兩個可選引數與res.write()相同。如果不呼叫這個函式,客戶端將用於處於等待狀態。
http client
http模組提供了兩個函式 http.request
和 http.get
,功能是作為客戶端向http伺服器發起請求。
http.request(options,callback)
options是一個類似關聯陣列的物件,表示請求的引數,callback作為回撥函式,需要傳遞一個引數,為http.ClientResponse的例項,http.request返回一個http.ClientRequest的例項。
options常用的引數有host、port(預設為80)、method(預設為GET)、path(請求的相對於根的路徑,預設是“/”,其中querystring應該包含在其中,例如/search?query=byvoid)、headers(請求頭內容)
var http=require("http");var options={ hostname:"cn.bing.com", port:8080 } var req=http.request(options,function(res){ res.setEncoding("utf-8"); res.on("data",function(chunk){ console.log(chunk.toString()) }); console.log(res.statusCode); }); req.on("error",function(err){ console.log(err.message); }); req.end();
傳送POST請求(模擬了嚮慕課網發起評論的功能,headers請使用開發者工具從請求中獲取,基本上是參考scott老師的程式碼)
var http=require("http"); var querystring=require("querystring"); var postData=querystring.stringify({ "content":"just a test", "mid":8837 }); var options={ hostname:"www.imooc.com", port:80, path:"/course/document", method:"POST", headers:{ "Accept":"application/json, text/javascript, */*; q=0.01", "Accept-Encoding":"gzip, deflate", "Accept-Language":"zh-CN,zh;q=0.8", "Connection":"keep-alive", "Content-Length":postData.length, "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8", "Cookie":"imooc_uuid=6cc9e8d5-424a-4861-9f7d-9cbcfbe4c6ae; imooc_isnew_ct=1460873157; loginstate=1; apsid=IzZDJiMGU0OTMyNTE0ZGFhZDAzZDNhZTAyZDg2ZmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjkyOTk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNmNmFhMmVhMTYwNzRmMjczNjdmZWUyNDg1ZTZkMGM1BwhXVwcIV1c%3DMD; PHPSESSID=thh4bfrl1t7qre9tr56m32tbv0; Hm_lvt_f0cfcccd7b1393990c78efdeebff3968=1467635471,1467653719,1467654690,1467654957; Hm_lpvt_f0cfcccd7b1393990c78efdeebff3968=1467655022; imooc_isnew=2; cvde=577a9e57ce250-34", "Host":"www.imooc.com", "Origin":"http://www.imooc.com", "Referer":"http://www.imooc.com/video/8837", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2763.0 Safari/537.36", "X-Requested-With":"XMLHttpRequest", } } var req=http.request(options,function(res){ res.on("data",function(chunk){ console.log(chunk); }); res.on("end",function(){ console.log("### end ##"); }); console.log(res.statusCode); }); req.on("error",function(err){ console.log(err.message); }) req.write(postData); req.end();
http.get(options,callback)
這個方法是http.request方法的簡化版,唯一的區別是http.get自動將請求方法設為了GET請求,同時不需要手動呼叫req.end(),但是需要記住的是,如果我們使用http.request方法時沒有呼叫end方法,伺服器將不會收到資訊。
request
可以將request模組想象成一個簡化版的第三方類http模組,同時支援https 和重定向,戳這裡區官網。下文列出幾個能夠讓你快速上手的知識點。
安裝
npm install request --save
var request = require('request');
API
GET
request(url,function(error,response,body){ if(!error && response.statusCode == 200){ //輸出返回的內容 console.log(body); } });
POST
var options = { uri: 'https://www.googleapis.com/urlshortener/v1/url', method: 'POST', json: { "longUrl": "http://www.google.com/" } }; request({ url: 'http://xxx.xxx.com', method: 'POST', body: formData }, function(error, response, body) { if (!error && response.statusCode == 200) { //輸出返回的內容 console.log(body); } });
流
任何響應都可以輸出到檔案流。
request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))
反過來,也可以將檔案傳給PUT或POST請求。未提供header的情況下,會檢測檔案字尾名,在PUT請求中設定相應的content-type。
fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))
表單
request支援 application/x-www-form-urlencoded
和 multipart/form-data
實現表單上傳。
x-www-form-urlencoded:
request.post('http://service.com/upload', {form:{key:'value'}}) 或者: request.post('http://service.com/upload').form({key:'value'})
multipart/form-data
var r = request.post('http://service.com/upload') var form = r.form() form.append('my_field', 'my_value') form.append('my_buffer', new Buffer([1, 2, 3])) form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png')) form.append('remote_file', request('http://google.com/doodle.png'))
superagent
superagent它是一個強大並且可讀性很好的輕量級ajax API,是適用於nodejs環境的一個關於HTTP方面的庫。
安裝
npm install superagent --save
簡單使用
一個請求的初始化可以用請求物件裡合適的方法來執行,然後呼叫end()來發送請求。
var superagent = require('superagent');
superagent
.post('/api')
.send({
'key': 'value'
})
.set('header_key', 'header_value')
.end(function(err, res) {
if (err) {
//do something
} else {
//do something
}
})
或
superagetn
.get(''http://example.com/search'')
.end(function(res){ });
API
請求方法的引數可以直接使用多個key/value,也可以分多次呼叫請求方法每次傳遞一對key/valu或者key/value字串
GET
//接下來四種方法所形成的URL為/api?name=An&age=20&sex=male //第一種 superagent .get(/api) .query({name:'liang'}) .query({age:18}) .query({sex:'female'}) .end(function(res){ }) //第二種 superagent .get(/api) .query({name:'liang',age:18,sex:'female'}) .end(function(res){ }) //第三種 superagent .get(/api) .query('name=liang&age=18&sex=female') .end(function(res){ }) //第四種 superagent .get(/api) .query('name=liang') .query('age=18') .query('sex=female') .end(function(res){ })
POST
superagent .post('/api') .set('Content-Type','application/json') .send('{"name":"An","age":20,"sex":"male"}') .end(cb) //等價於 下面的寫法,因為json是預設的 Content-Type superagent .post('/api') .send({name:"An",age:20,sex:"male"}) .end(cb) //等價於 ==> superagent .post('/api') .send({name:"An"}) .send({age:20}) .sex({sex:'male'}) .end(cb)
superagent的請求資料格式化是可以擴充套件的,不過預設支援form和json兩種格式,想傳送資料以application/x-www-form-urlencoded型別的話,則可以簡單的呼叫.type()方法傳遞form引數就行,這裡預設是json,下面的請求將會發送post name=a&age=18:
request .post('/user') .type('form') .send({ name: 'tj' }) .send({ pet: 'tobi' }) .end(callback)
post && get
當用.send(obj)方法來發送一個post請求,並且希望傳遞一些查詢字串,可以呼叫.query()方法,比如向?format=json&dest=/login傳送post請求:
request
.post('/')
.query({ format: 'json' })
.query({ dest: '/login' })
.send({ post: 'data', msg: 'hello' })
.end(callback);
請求設定
設定請求頭:呼叫set()方法,引數傳遞一組鍵值對
superagent .get('/api') .set({ 'Referer','https://www.google.com', 'Accept','image/webp,image/*,*/*;q=0.8' }) .end(function(req,res){ //do something })
Response
響應一般會提供很多有用的標識以及屬性,都在response物件裡,按照respone.text,解析後的response.body,頭欄位,一些標識的順序來排列。
- res.text
包含未被解析的響應資料
- res.body
包含解析的資料,跟請求資料自動序列化一樣,響應資料也會自動的解析,
當為一個Content-Type。定義一個解析器後,就能自動解析,預設解析包
含application/json和application/x-www-form-urlencoded,可以
通過訪問res.body來訪問解析物件。
- res.header
響應頭,res.header包含解析之後的響應頭資料,鍵值都是node處理成小
寫字母形式,比如res.header['content-length'].
- res.type & res.charset 型別和編碼格式
Content-Type響應頭欄位是一個特列,伺服器提供res.type來訪問它,
預設res.charset是空的,如果有的話,則自動填充,例如Content-Type
值為text/html; charset=utf8,則res.type為text/html,res.charst
為utf8.
- res.status狀態碼
其他設定
req.abort() 終止請求
req.timeout(ms) 暫停請求 ms 表示毫秒為單位的時間
管道資料
nodejs客戶端允許使用一個請求流來輸送資料,比如請求一個檔案作為輸出流:
var request = require('superagent') ,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') , fs = require('fs'); var stream = fs.createWriteStream('path/to/my.json'); var req = request.get('/some.json'); req.pipe(stream);
錯誤處理
當傳送錯誤時,superagent首先會檢查回撥函式的引數數量,當err引數提供的話,引數就是兩個,如下:
request .post('/upload') .attach('image', 'path/to/tobi.png') .end(function(err, res){ }); `` 當省略了回撥函式,或者回調只有一個引數的話,可以新增error事件的處理. ```js request .post('/upload') .attach('image', 'path/to/tobi.png') .on('error', handle) .end(function(res){ });