HTTP模塊簡介

以下內(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 。

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);
  });
});
  1. 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 文檔玩焰。
  1. 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ù)糖赔。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萍丐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子放典,更是在濱河造成了極大的恐慌逝变,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奋构,死亡現(xiàn)場離奇詭異壳影,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)声怔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門态贤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人醋火,你說我怎么就攤上這事悠汽。” “怎么了芥驳?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵柿冲,是天一觀的道長。 經(jīng)常有香客問我兆旬,道長假抄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任丽猬,我火速辦了婚禮宿饱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脚祟。我一直安慰自己谬以,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布由桌。 她就那樣靜靜地躺著为黎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪行您。 梳的紋絲不亂的頭發(fā)上铭乾,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音娃循,去河邊找鬼炕檩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛捌斧,可吹牛的內(nèi)容都是我干的笛质。 我是一名探鬼主播吹泡,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼经瓷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洞难,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤舆吮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后队贱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體色冀,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年柱嫌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锋恬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡编丘,死狀恐怖与学,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘉抓,我是刑警寧澤索守,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站抑片,受9級特大地震影響卵佛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜敞斋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一截汪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧植捎,春花似錦衙解、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至医咨,卻和暖如春枫匾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拟淮。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工干茉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人很泊。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓角虫,卻偏偏與公主長得像沾谓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子戳鹅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355