NodeJS初學者教程:Node.js之HTTP
來自: http://ourjs.com/detail/56dfb29588feaf2d031d2488
前言
在開始node.js(以后簡稱node)之前,我猶豫了好久:到底我第一篇用什么主題開始,是node的整體框架還是Hello world還是一堆廢話呢。最后我決定還是直接上API,簡單粗暴有效果,因為大家用node除了node有強大的社區之外,還因為node的API簡單易用,容易上手。OK,下面我們就開始node之旅。
HTTP
大部分的node使用者,都是用node來做Web API的,而HTTP模塊是提供Web API的基礎。為了支持所有的HTTP應用,node中的HTTTP模塊提供的API是偏向底層化的。利用HTTP模塊,我們可以簡單快速搭建一個Web Server。
引入HTTP模塊
在node 官方API 中,第一句話是這么說的:“To use the HTTP server and client one must require('http')”,如果想要使用HTTP的服務器端和客戶端的方法,首先第一步我們需要使用“require('http')”引入HTTP模塊。
var http = require('http');
我們引入了HTTP模塊,并且將結果賦值給了http變量,這樣http就擁有了HTTP模塊的所有方法和對象(關于requirejs的原理,與主題沒什么太大關系,就略過。但是記住,require的過程是一個同步的過程,而前端的引入define是一個異步過程)。
創建一個簡單的Web Server
在HTTP模塊中,node提供給我們一個createServer方法,來創建一個Web Server。代碼可以這樣寫:
var http = require('http'); var server = http.createServer();server.on('request', function(request, response) { console.log('有人請求了服務器!');
response.writeHead(200, { 'Content-Type': 'text/plain' }); response.write('OK'); response.end(); });
server.listen(88, function() { console.log('服務器已經開始監聽88端口'); });</pre>
上面的代碼:
-
我們首先用http.createServer(從express4.X時代進入的同學可能有點陌生,express在4.X后把該方法封裝到了其內部,但是我們在創建HTTPS連接時,還是要用到此方法)函數創建了一個服務器對象。
-
然后監聽了該對象的“request”事件(了解過node的同學應該知道,node是事件驅動的,一般事件觸發和事件接收是成對出現的),這邊我們定義接收事件。也就是說,如果當前服務器對象監聽的端口一旦被請求,那么就會觸發該事件。“request”的回調中,我們首先調用了response.writeHead方法:該方法的第一個參數表示HTTP的響應狀態(200)表示一切正常;第二個參數是“Content-Type”,表示我響應給客戶端的內容類型。然后我們調用了response.write方法,寫入我們需要傳遞給客戶端的內容。最后一步我們調用了response.end,表示此次請求已處理完成。
-
最后我們調用了server.listen函數,此函數有兩個參數,第一個參數表示我們需要監聽的端口,第二個參數是回調函數(其實是listening事件),當監聽開啟后立刻觸發。所以當我們用node命令來啟動當前的js文件后,在cmd窗口會馬上出現“服務器已經開始監聽88端口”的字樣,表明服務器正常且已經開始監聽88端口。
當我們請求了服務器的88端口后,服務器會響應給我們“OK”字符串。到此我們的簡單Web Server已經搭建完成,但是此時我們的代碼還不具備任何業務能力,但是我們只要把代碼稍稍修改下,就可以入眼了:
var url = require('url'); var http = require('http'); var server = http.createServer();function bizLogic(url) {
if (url.path.toLowerCase() === '/login') { return { msg: '登錄成功!' } } else if (url.path.toLowerCase() === '/user') { return { msg: [{ name: '小明', class: '一年級1班' }, { name: '小紅', class: '一年級3班' }] } } else { return { msg: 404 } } }
server.on('request', function(request, response) {
if ('url' in request) { response.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
response.write(JSON.stringify(bizLogic(url.parse(request.url))), 'utf8');
} else { response.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
response.write('找不到頁面');
}
response.end(); });
server.listen(88, function() { console.log('服務器已經開始監聽88端口'); });</pre>
- 首先我們引入了一個新的模塊:“url”。這個模塊主要幫助我們將整理請求url中的信息即url.parse方法(如果有興趣,你可以去研究下url的所有API,其實就幾個)。url.parse轉換完成之后,url參數信息就變成這樣了:
</ul>
{ protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: null, query: null, pathname: '/login', path: '/login', href: '/login' }
這里我們取了我們需要的參數:path,簡單的根據path來判斷接口。然后響應客戶端不同的內容。
-
如果我們直接“response.write(bizLogic(url.parse(request.url)), 'utf8');”系統就會報錯,response.write第一個參數必須是string或者Buffer,所以我們在這里將object轉換成了string,然后接著write。
-
最后我們直接結束連接(response.end)。
好了,一個簡單的業務已經處理完成了,但是實際開過過程沒有那么簡單:HTTP方法有“GET”、“POST”、“PUT”等等。這也是要我們來處理的,所以如果我們用node來做Web API的話,一般會選擇第三方的Web框架如:express、koa、restify等等(好吧,這是后話了,以后有機會我會去研究下他們各自的源碼給大家分享)。
http.request
上面講了那么多,其實都是在將HTTP Server的內容,既然node那么強大,當然除了可以作為HTTP Server之外,也可以做HTTP Client。現在就以我們剛剛建立的服務端為服務器,利用http.request來請求剛剛的服務器,看看會發生什么事情。
var http = require('http');var option, req;
option = { host: '127.0.0.1', port: 88, path: '/login' };
req = http.request(option, function(res){
if(res.statusCode === 200) { console.log('ok'); }
res.on('data', function(chunk){ console.log('the result is ', chunk.toString()); }); });
req.on('error', function(error){ console.log('something is wrong:', error.message); });
req.end();</pre>
-
首先我們當然要引入http模塊,之后又定義了option和req對象。option是我們一會需要請求服務器的一些參數,req是請求對象用于接收request返回的結果(并不是請求的結果)
-
option參數是一個對象,使我們對此次請求設置的參數,包括:我們要請求的主機地址(host,注意此處不需要“ http://”)、請求的請求的端口號(port)、請求的路由及參數(path )
-
調用http.request請求,第一個參數就是我們剛剛設置的option,第二個參數是回調函數(用于返回我們請求的結果)
-
回調函數有一個參數,這個參數是http.IncomingMessage的實例。字面意思就是“進來的消息”,也就是請求收到的響應結果對象。http.IncomingMessage實現了可讀流接口,也就是說我們的“data”事件被觸發時,回調函數中的結果是Buffer(關于Buffer我以后會詳細解釋---另一篇博客)對象,所以我在打印的時候調用了Bufer對象的toString方法把Buffer對象轉換成字符串打印出來。
-
最后我們監聽了req的錯誤事件(在node中,我們如果出錯不定義error事件的話,程序就會直接把錯誤拋出,如果沒有做錯誤捕獲的話,node程序就會崩掉),用于記錄錯誤信息。最后我們關閉了請求。
-
http.get是http.request的“GET”方法的方便版本,只能執行“GET”操作。
-
http.ClientRequest的“connection”事件,當TCP(HTTP其實也是TCP)的連接通道建立之后觸發的事件。
-
http.ClientRequest的“close”事件,當服務端關閉連接時觸發。
-
http.ServerResponse的setTimeout方法,設置響應超時。如果沒有設置的話,只能等待socket或服務器超時。(還有很多方法和事件。就不一一列舉了。)
</ul>
經過這幾個簡單地步驟,我們此次請求就完成了,如果服務端沒有錯誤的話,使用node命令運行我們剛剛寫好的客戶端代碼,在cmd窗口先會打印“ok”,然后帶打印“the result is {"msg":"登錄成功!"}”。一旦請求出錯,就會直接出發我們的“error”事件。當然如果不想使用原生的HTTP模塊的話,你也可以使用第三方的包: request 、 superagent 等都是非常優秀且方便的第三方包。(原生的HTTP模塊可能會遇到各種奇葩的問題,使用第三方包的話嗎,坑會少一些)。
其他方法和事件
總結
在node v0.10.x時代,HTTP模塊的穩定系數就已經是3了(一般node的API有4個級別,分別是:Deprecated-0、Experimental-1、Stable-2、Locked-3).所以說HTTP模塊的API非常基礎和穩定的。我們使用HTTP模塊時,可以很放心的使用其方法和事件,不必擔心版本兼容問題。需要提到的是,學習node的最好最捷徑的方法就是學會讀官方API,官方API雖然是英文的,但是里面的解釋和英文單詞都是很戳中要點和易于理解的。另外本文也會在我的 博客 上同步更新,歡迎訪問。
</div>