淺析nodejs的http模塊


我們知道傳統的HTPP服務器會由Aphche、Nginx、IIS之類的軟件來擔任,但是nodejs並不需要,nodejs提供了http模塊,自身就可以用來構建服務器,而且http模塊是由C++實現的,性能可靠。我們在nodejs中的教程或者書籍中常常會通過一個簡易的http服務器來作為開頭的學習,就像下面這個例子

var http=require("http"); http.createServer(function(req,res){ res.writeHead(200,{ "content-type":"text/plain" }); res.write("hello nodejs"); res.end(); }).listen(3000); 

打開瀏覽器,輸入localhost:3000我們就可以看到屏幕上的hello nodejs了,這表明這個最簡單的nodejs服務器已經搭建成功了。
nodejs中的http模塊中封裝了一個HTPP服務器和一個簡易的HTTP客戶端,http.Server是一個基於事件的http服務器,http.request則是一個http客戶端工具,用於向http服務器發起請求。而上面的createServer方法中的參數函數中的兩個參數req和res則是分別代表了請求對象和響應對象。其中req是http.IncomingMessage的實例,res是http.ServerResponse的實例,這可以從nodejs中的源碼中獲取這個信息(當然我們作為初學者,自然是不太可能讀懂其源碼,推薦慕課網的課程《進擊的nodjs基礎(一)》,scott老師會帶我們基本走一下http模塊的源碼,會有一個大概的了解)。以下將從http服務器和http客戶端簡要走一遍http模塊

一、http服務器####


文章開頭使用的createServer方法返回了一個http.Server對象,這其實是一個創建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.Server的事件吧

 

 

1、http.Server的事件######

正如我們上面所說,http.Server是一個基於事件的服務器,她是繼承自EventEmitter,事實上,nodejs中大部分模塊都繼承自EventEmitter,包括fs、net等模塊,這也是為什么說nodejs基於事件驅動(關於EventEmitter的更多內容可以在官方api下的events模塊找到),http.Server提供的事件如下:

  • request:當客戶端請求到來時,該事件被觸發,提供兩個參數req和res,表示請求和響應信息,是最常用的事件
  • connection:當TCP連接建立時,該事件被觸發,提供一個參數socket,是net.Socket的實例
  • close:當服務器關閉時,觸發事件(注意不是在用戶斷開連接時)

正如上面我們所看到的request事件是最常用的,而參數req和res分別是http.IncomingMessage和http.ServerResponse的實例,那么我們來看看這兩個類吧

2、http.IncomingMessage######

http.IncomingMessage是HTTP請求的信息,是后端開發者最關注的內容,一般由http.Server的request事件發送,並作為第一個參數傳遞,http請求一般可以分為兩部分:請求頭和請求體(更多關於http協議的知識可以查看我之前的筆記http入門與挖坑);其提供了3個事件,如下

  • data:當請求體數據到來時,該事件被觸發,該事件提供一個參數chunk,表示接受的數據,如果該事件沒有被監聽,則請求體會被拋棄,該事件可能會被調用多次(這與nodejs是異步的有關系)
  • end:當請求體數據傳輸完畢時,該事件會被觸發,此后不會再有數據
  • close:用戶當前請求結束時,該事件被觸發,不同於end,如果用戶強制終止了傳輸,也是用close

http.IncomingMessage的屬性如下:

 
 
3、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服務器,我們看一看http的客戶端吧

二、http客戶端####


http模塊提供了兩個函數http.request和http.get,功能是作為客戶端向http服務器發起請求。

1、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:80 } 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(); 

我們運行這段代碼我們在控制台可以發現,必應首頁的html代碼已經呈現出來了。

接下來我們來做一個關於POST請求的代碼。

var http=require("http"); var querystring=require("querystring"); var postData=querystring.stringify({ "content":"我真的只是測試一下", "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("評論完畢!"); }); console.log(res.statusCode); }); req.on("error",function(err){ console.log(err.message); }) req.write(postData); req.end(); 

這段代碼我們模擬了向慕課網發起評論的功能,但是因為慕課網對於這種發起方式專門做了重定向,所以我們並不能在評論區看到我們的評論(另外在一些對這種請求沒有做處理的網站,擅自發起評論,很可能會當做網絡攻擊,所以不要玩火,你懂得),但是這段代碼卻很可以說明這個過程。另外,代碼中的請求頭和postData里面的鍵我們都是可以通過開發者工具network下找到的。

2、http.get(options,callback)######

這個方法是http.request方法的簡化版,唯一的區別是http.get自動將請求方法設為了GET請求,同時不需要手動調用req.end(),但是需要記住的是,如果我們使用http.request方法時沒有調用end方法,服務器將不會收到信息。因為http.get和http.request方法都是放回一個http.ClientRequest對象,所以我們來看一下這兩個對象。

3、http.ClientRequest######

http.ClientRequest是由http.request或者是http.get返回產生的對象,表示一個已經產生而且正在進行中的HTPP請求,提供一個response事件,也就是我們使用http.get和http.request方法中的回調函數所綁定的對象,我們可以顯式地綁定這個事件的監聽函數

var http=require("http"); var options={ hostname:"cn.bing.com", port:80 } var req=http.request(options); req.on("response",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(); 

http.ClientRequest也提供了write和end函數,用於向服務器發送請求體,通常用於POST、PUT等操作,所有寫操作都必須調用end函數來通知服務器,否則請求無效。此外,這個對象還提供了abort()、setTimeout()等方法,具體可以參考文檔

4、http.ClientReponse######

與http.ServerRequest相似,提供了三個事件,data、end、close,分別在數據到達、傳輸結束和連接結束時觸發,其中data事件傳遞一個參數chunk,表示接受到的數據。其屬性如下

 
 

此外,這個對象提供了幾個特殊的函數

  • response。setEncoding([encoding]):設置默認的編碼,當data事件被觸發時,數據將會以encoding編碼,默認值是null,也就是不編碼,以buffer形式存儲
  • response.pause():暫停結束數據和發送事件,方便實現下載功能
  • response.resume():從暫停的狀態中恢復

好的,到這里http模塊的主要功能已經介紹完了。我們接着來做一個練習,我們知道中山大學今年院系改革,弄得我這個中大學生也不知道中大到底有那些學院了,是的,我們打開官網我們會發現有哪些學院,但是我想用http模塊把里面的學院名給扒下來,好的,那我就做了以下代碼

var cheerio=require("cheerio"); var http=require("http"); var fs=require("fs"); var options="http://www.sysu.edu.cn/2012/cn/jgsz/yx/index.htm"; var htmlData="" var req=http.request(options,function(res){ res.on("data",function(chunk){ htmlData+=chunk; }); res.on("end",function(){ var $=cheerio.load(htmlData); var textcontent=$("tr").text(); fs.writeFile("./school.txt",textcontent,"utf-8") }); }); req.end(); 

之后我們就可在school.txt文件中看到所有的學院了,怎么樣,是不是很有成就感,題外話,我這里用了一個外部的模塊cheerio,這個模塊可以讓我們像jquery一樣操作html代碼,這個模塊的介紹就到這里了(大家不要用這個模塊的知識去干壞事哦,否則我可不負責┑( ̄Д  ̄)┍)。



作者:忽如寄
鏈接:https://www.jianshu.com/p/ab2741f78858
來源:簡書


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM