以下內(nèi)容來自:Node.js開發(fā)指南逝段。在此感謝原作者提供的幫助稠肘!
HTTP 服務(wù)器與客戶端
Node.js 標(biāo)準(zhǔn)庫提供了 http 模塊,其中封裝了一個(gè)高效的 HTTP 服務(wù)器和一個(gè)簡易的HTTP 客戶端。 http.Server 是一個(gè)基于事件的 HTTP 服務(wù)器,它的核心由 Node.js 下層 C++部分實(shí)現(xiàn)县昂,而接口由 JavaScript 封裝,兼顧了高性能與簡易性陷舅。 http.request 則是一個(gè)
HTTP 客戶端工具七芭,用于向 HTTP 服務(wù)器發(fā)起請求。
HTTP 服務(wù)器
http.Server 是 http 模塊中的 HTTP 服務(wù)器對象蔑赘,用 Node.js 做的所有基于 HTTP 協(xié)議的系統(tǒng),如網(wǎng)站预明、社交應(yīng)用甚至代理服務(wù)器缩赛,都是基于 http.Server 實(shí)現(xiàn)的。它提供了一套封裝級別很低的 API撰糠,僅僅是流控制和簡單的消息解析酥馍,所有的高層功能都要通過它的
接口來實(shí)現(xiàn)。
我們在 3.1.3 節(jié)中使用 http 實(shí)現(xiàn)了一個(gè)服務(wù)器:
//app.js
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
}).listen(3000);
console.log("HTTP server is listening at port 3000.");
這段代碼中阅酪, http.createServer 創(chuàng)建了一個(gè) http.Server 的實(shí)例旨袒,將一個(gè)函數(shù)作為 HTTP 請求處理函數(shù)。這個(gè)函數(shù)接受兩個(gè)參數(shù)术辐,分別是請求對象( req )和響應(yīng)對象( res )砚尽。在函數(shù)體內(nèi), res 顯式地寫回了響應(yīng)代碼 200 (表示請求成功)辉词,指定響應(yīng)頭為'Content-Type': 'text/html' 必孤,然后寫入響應(yīng)體 '<h1>Node.js</h1>' ,通過 res.end結(jié)束并發(fā)送瑞躺。最后該實(shí)例還調(diào)用了 listen 函數(shù)敷搪,啟動(dòng)服務(wù)器并監(jiān)聽 3000 端口兴想。
- http.Server 的事件
http.Server 是一個(gè)基于事件的 HTTP 服務(wù)器,所有的請求都被封裝為獨(dú)立的事件赡勘,開發(fā)者只需要對它的事件編寫響應(yīng)函數(shù)即可實(shí)現(xiàn) HTTP 服務(wù)器的所有功能嫂便。它繼承自EventEmitter ,提供了以下幾個(gè)事件闸与。- request :當(dāng)客戶端請求到來時(shí)毙替,該事件被觸發(fā),提供兩個(gè)參數(shù) req 和 res 几迄,分別是http.ServerRequest 和 http.ServerResponse 的實(shí)例蔚龙,表示請求和響應(yīng)信息。
- connection :當(dāng) TCP 連接建立時(shí)映胁,該事件被觸發(fā)木羹,提供一個(gè)參數(shù) socket ,為net.Socket 的實(shí)例解孙。 connection 事件的粒度要大于 request 坑填,因?yàn)榭蛻舳嗽贙eep-Alive 模式下可能會(huì)在同一個(gè)連接內(nèi)發(fā)送多次請求。
- close :當(dāng)服務(wù)器關(guān)閉時(shí)弛姜,該事件被觸發(fā)脐瑰。注意不是在用戶連接斷開時(shí)。除此之外還有 checkContinue 廷臼、 upgrade 苍在、 clientError 事件,通常我們不需要關(guān)心荠商,只有在實(shí)現(xiàn)復(fù)雜的 HTTP 服務(wù)器的時(shí)候才會(huì)用到寂恬。在這些事件中,最常用的就是 request 了莱没,因此 http 提供了一個(gè)捷徑:
http.createServer([requestListener])
初肉,功能是創(chuàng)建一個(gè) HTTP 服務(wù)器并將requestListener 作為 request 事件的監(jiān)聽函數(shù),這也是我們前面例子中使用的方法饰躲。
事實(shí)上它顯式的實(shí)現(xiàn)方法是:
//httpserver.js
var http = require('http');
var server = new http.Server();
server.on('request', function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
});
server.listen(3000);
console.log("HTTP server is listening at port 3000.");
- http.ServerRequest
http.ServerRequest 是 HTTP 請求的信息牙咏,是后端開發(fā)者最關(guān)注的內(nèi)容。它一般由http.Server 的 request 事件發(fā)送嘹裂,作為第一個(gè)參數(shù)傳遞妄壶,通常簡稱 request 或 req 。ServerRequest 提供一些屬性焦蘑,表 4-2 中列出了這些屬性盯拱。HTTP 請求一般可以分為兩部分:請求頭(Request Header)和請求體(Requset Body)。以上內(nèi)容由于長度較短都可以在請求頭解析完成后立即讀取。而請求體可能相對較長狡逢,需要一定的時(shí)間傳輸宁舰,因此 http.ServerRequest 提供了以下3個(gè)事件用于控制請求體
傳輸。- data :當(dāng)請求體數(shù)據(jù)到來時(shí)奢浑,該事件被觸發(fā)蛮艰。該事件提供一個(gè)參數(shù) chunk ,表示接收到的數(shù)據(jù)雀彼。如果該事件沒有被監(jiān)聽壤蚜,那么請求體將會(huì)被拋棄。該事件可能會(huì)被調(diào)
用多次徊哑。 - end :當(dāng)請求體數(shù)據(jù)傳輸完成時(shí)袜刷,該事件被觸發(fā),此后將不會(huì)再有數(shù)據(jù)到來莺丑。
- close : 用戶當(dāng)前請求結(jié)束時(shí)著蟹,該事件被觸發(fā)。不同于 end 梢莽,如果用戶強(qiáng)制終止了傳輸萧豆,也還是調(diào)用 close 。
- data :當(dāng)請求體數(shù)據(jù)到來時(shí)奢浑,該事件被觸發(fā)蛮艰。該事件提供一個(gè)參數(shù) chunk ,表示接收到的數(shù)據(jù)雀彼。如果該事件沒有被監(jiān)聽壤蚜,那么請求體將會(huì)被拋棄。該事件可能會(huì)被調(diào)
ServerRequest 的屬性:
- complete: 客戶端請求是否已經(jīng)發(fā)送完成
- httpVersion: HTTP 協(xié)議版本昏名,通常是 1.0 或 1.1
- method: HTTP 請求方法涮雷,如 GET、POST轻局、PUT洪鸭、DELETE 等
- url: 原始的請求路徑,例如 /static/image/x.jpg 或 /user?name=byvoid
- headers: HTTP 請求頭
- trailers: HTTP 請求尾(不常見)
- connection: 當(dāng)前 HTTP 連接套接字仑扑,為 net.Socket 的實(shí)例
- socket: connection 屬性的別名
- client: client 屬性的別名
獲取 GET 請求內(nèi)容
注意卿嘲, http.ServerRequest 提供的屬性中沒有類似于 PHP 語言中的 $_GET 或$_POST 的屬性,那我們?nèi)绾谓邮芸蛻舳说谋韱握埱竽胤虮冢坑捎?GET 請求直接被嵌入在路徑中,URL是完整的請求路徑沃疮,包括了 ? 后面的部分盒让,因此你可以手動(dòng)解析后面的內(nèi)容作為 GET請求的參數(shù)。Node.js 的 url 模塊中的 parse 函數(shù)提供了這個(gè)功能司蔬,例如:
//httpserverrequestget.js
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);
在瀏覽器中訪問 http://127.0.0.1:3000/username=byvoid&email=byvoid@byvoid.com
邑茄,我們可以看到瀏覽器返回的結(jié)果:
{ search: '?name=byvoid&email=byvoid@byvoid.com',
query: { name: 'byvoid', email: 'byvoid@byvoid.com' },
pathname: '/user',
path: '/user?name=byvoid&email=byvoid@byvoid.com',
href: '/user?name=byvoid&email=byvoid@byvoid.com' }
通過 url.parse ,原始的 path 被解析為一個(gè)對象俊啼,其中 query 就是我們所謂的 GET請求的內(nèi)容肺缕,而路徑則是 pathname 。
獲取 POST 請求內(nèi)容
HTTP 協(xié)議 1.1 版本提供了8種標(biāo)準(zhǔn)的請求方法,其中最常見的就是 GET 和 POST同木。相比GET 請求把所有的內(nèi)容編碼到訪問路徑中浮梢,POST 請求的內(nèi)容全部都在請求體中。
http.ServerRequest 并沒有一個(gè)屬性內(nèi)容為請求體彤路,原因是等待請求體傳輸可能是一件耗時(shí)的工作秕硝,譬如上傳文件。而很多時(shí)候我們可能并不需要理會(huì)請求體的內(nèi)容洲尊,惡意的 POST請求會(huì)大大消耗服務(wù)器的資源远豺。所以 Node.js 默認(rèn)是不會(huì)解析請求體的,當(dāng)你需要的時(shí)候坞嘀,
需要手動(dòng)來做躯护。讓我們看看實(shí)現(xiàn)方法:
//httpserverrequestpost.js
var http = require('http');
var querystring = require('querystring');
var util = require('util');
http.createServer(function(req, res) {
var post = '';
req.on('data', function(chunk) {
post += chunk;
});
req.on('end', function() {
post = querystring.parse(post);
res.end(util.inspect(post));
});
}).listen(3000);
上面代碼并沒有在請求響應(yīng)函數(shù)中向客戶端返回信息,而是定義了一個(gè) post 變量丽涩,用于在閉包中暫存請求體的信息棺滞。通過 req 的 data 事件監(jiān)聽函數(shù),每當(dāng)接受到請求體的數(shù)據(jù)内狸,就累加到 post 變量中检眯。在 end 事件觸發(fā)后,通過 querystring.parse 將 post 解析為真正的 POST 請求格式昆淡,然后向客戶端返回锰瘸。
- http.ServerResponse
http.ServerResponse 是返回給客戶端的信息,決定了用戶最終能看到的結(jié)果昂灵。它也是由 http.Server 的 request 事件發(fā)送的避凝,作為第二個(gè)參數(shù)傳遞,一般簡稱為response 或 res 眨补。
http.ServerResponse 有三個(gè)重要的成員函數(shù)管削,用于返回響應(yīng)頭、響應(yīng)內(nèi)容以及結(jié)束請求撑螺。- response.writeHead(statusCode, [headers]) :向請求的客戶端發(fā)送響應(yīng)頭含思。statusCode 是 HTTP 狀態(tài)碼,如 200 (請求成功)甘晤、404 (未找到)等含潘。 headers是一個(gè)類似關(guān)聯(lián)數(shù)組的對象,表示響應(yīng)頭的每個(gè)屬性线婚。該函數(shù)在一個(gè)請求內(nèi)最多只能調(diào)用一次遏弱,如果不調(diào)用,則會(huì)自動(dòng)生成一個(gè)響應(yīng)頭塞弊。
- response.write(data, [encoding]) :向請求的客戶端發(fā)送響應(yīng)內(nèi)容漱逸。 data 是一個(gè) Buffer 或字符串泪姨,表示要發(fā)送的內(nèi)容。如果 data 是字符串饰抒,那么需要指定encoding 來說明它的編碼方式肮砾,默認(rèn)是 utf-8 。在 response.end 調(diào)用之前循集,response.write 可以被多次調(diào)用唇敞。
- response.end([data], [encoding]) :結(jié)束響應(yīng),告知客戶端所有發(fā)送已經(jīng)完成咒彤。當(dāng)所有要返回的內(nèi)容發(fā)送完畢的時(shí)候疆柔,該函數(shù) 必須 被調(diào)用一次。它接受兩個(gè)可選參數(shù)镶柱,意義和 response.write 相同旷档。如果不調(diào)用該函數(shù),客戶端將永遠(yuǎn)處于等待狀態(tài)歇拆。
HTTP 客戶端
http 模塊提供了兩個(gè)函數(shù) http.request 和 http.get 鞋屈,功能是作為客戶端向 HTTP服務(wù)器發(fā)起請求。
- http.request(options, callback) 發(fā)起 HTTP 請求故觅。接受兩個(gè)參數(shù)厂庇, option 是一個(gè)類似關(guān)聯(lián)數(shù)組的對象,表示請求的參數(shù)输吏, callback 是請求的回調(diào)函數(shù)权旷。 option
常用的參數(shù)如下所示。 - host :請求網(wǎng)站的域名或 IP 地址贯溅。
- port :請求網(wǎng)站的端口拄氯,默認(rèn) 80。
- method :請求方法它浅,默認(rèn)是 GET译柏。
- path :請求的相對于根的路徑,默認(rèn)是“ / ”姐霍。 QueryString 應(yīng)該包含在其中鄙麦。例如 /search?query=byvoid 。
- headers :一個(gè)關(guān)聯(lián)數(shù)組對象镊折,為請求頭的內(nèi)容黔衡。
- callback 傳遞一個(gè)參數(shù),為 http.ClientResponse 的實(shí)例腌乡。
http.request 返回一個(gè) http.ClientRequest 的實(shí)例。
下面是一個(gè)通過 http.request 發(fā)送 POST 請求的代碼:
//httprequest.js
var http = require('http');
var querystring = require('querystring');
var contents = querystring.stringify({
name: 'byvoid',
email: 'byvoid@byvoid.com',
address: 'Zijing 2#, Tsinghua University',
});
var options = {
host: 'www.byvoid.com',
path: '/application/node/post.php',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length' : contents.length
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (data) {
console.log(data);
});
});
req.write(contents);
req.end();
運(yùn)行后結(jié)果如下:
array(3) {
["name"]=>
string(6) "byvoid"
["email"]=>
string(17) "byvoid@byvoid.com"
["address"]=>
string(30) "Zijing 2#, Tsinghua University"
}
不要忘了通過 req.end() 結(jié)束請求夜牡,否則服務(wù)器將不會(huì)收到信息.
http.get(options, callback)
, http 模塊還提供了一個(gè)更加簡便的方法用于處理GET請求: http.get 与纽。它是 http.request 的簡化版侣签,唯一的區(qū)別在于 http.get自動(dòng)將請求方法設(shè)為了 GET 請求,同時(shí)不需要手動(dòng)調(diào)用 req.end() 急迂。
//httpget.js
var http = require('http');
http.get({host: 'www.byvoid.com'}, function(res) {
res.setEncoding('utf8');
res.on('data', function (data) {
console.log(data);
});
});
- http.ClientRequest
http.ClientRequest 是由 http.request 或 http.get 返回產(chǎn)生的對象影所,表示一個(gè)已經(jīng)產(chǎn)生而且正在進(jìn)行中的 HTTP 請求。它提供一個(gè)response 事件僚碎,即 http.request或 http.get 第二個(gè)參數(shù)指定的回調(diào)函數(shù)的綁定對象猴娩。我們也可以顯式地綁定這個(gè)事件的監(jiān)聽函數(shù):
//httpresponse.js
var http = require('http');
var req = http.get({host: 'www.byvoid.com'});
req.on('response', function(res) {
res.setEncoding('utf8');
res.on('data', function (data) {
console.log(data);
});
});
http.ClientRequest 像 http.ServerResponse 一樣也提供了 write 和 end 函數(shù),用于向服務(wù)器發(fā)送請求體勺阐,通常用于 POST卷中、PUT 等操作。所有寫結(jié)束以后必須調(diào)用 end函數(shù)以通知服務(wù)器渊抽,否則請求無效蟆豫。 http.ClientRequest 還提供了以下函數(shù)。
- request.abort() :終止正在發(fā)送的請求懒闷。
- request.setTimeout(timeout, [callback]) :設(shè)置請求超時(shí)時(shí)間十减, timeout 為毫秒數(shù)。當(dāng)請求超時(shí)以后愤估, callback 將會(huì)被調(diào)用帮辟。
此外還有 request.setNoDelay([noDelay]) request.setSocketKeepAlive
([enable] , [initialDelay]) 等函數(shù),具體內(nèi)容請參見 Node.js 文檔玩焰。
- http.ClientResponse
http.ClientResponse 與 http.ServerRequest 相似由驹,提供了三個(gè)事件 data 、end和 close 震捣,分別在數(shù)據(jù)到達(dá)荔棉、傳輸結(jié)束和連接結(jié)束時(shí)觸發(fā),其中 data 事件傳遞一個(gè)參數(shù)chunk 蒿赢,表示接收到的數(shù)據(jù)润樱。
ClientResponse 的屬性:
- statusCode: HTTP 狀態(tài)碼,如 200羡棵、404壹若、500
- httpVersion: HTTP 協(xié)議版本,通常是 1.0 或 1.1
- headers: HTTP 請求頭
- trailers: HTTP 請求尾(不常見)
http.ClientResponse 還提供了以下幾個(gè)特殊的函數(shù)皂冰。
- response.setEncoding([encoding]) :設(shè)置默認(rèn)的編碼店展,當(dāng) data 事件被觸發(fā)時(shí),數(shù)據(jù)將會(huì)以 encoding 編碼秃流。默認(rèn)值是 null 赂蕴,即不編碼,以 Buffer 的形式存儲(chǔ)舶胀。常用編碼為 utf8概说。
- response.pause() :暫停接收數(shù)據(jù)和發(fā)送事件碧注,方便實(shí)現(xiàn)下載功能。
- response.resume() :從暫停的狀態(tài)中恢復(fù)糖赔。