如果你熟悉.NET或其他類似平台的Web開發,你可能會像,建立一個Web伺服器有什麼,在Visual Studio中建立一個Web工程,點擊運行即可。事實的確是這樣,但請不要忘記,這樣的代價是,比如果說,你是用.NET開發Web應用,你就使用了完整的IIS作為你的Web伺服器基礎,這樣當你的應用程式發佈時就只能用IIS了。而如果使用獨立伺服器(利用System.Web.Hosting自己建立的話),你則必須處理各種HttpListener和對應線程,則會比較麻煩,畢竟.NET不是專注於面向Web的。 Node.js在這方面提供了方便且可自訂的途徑,你可以在其基礎上建立精巧且完全面向你應用的服務平台。
一、建立簡單的Web伺服器涉及到Node.js的一些基本知識點:
1、請求模組
在Node.js中,系統提供了許多有用的模組(當然也可以用JavaScript寫自己的模組,以後的章節我們將詳細講解),如http、url等。模組封裝特定的功能,提供對應的方法或屬性,要使用這些模組,需要先請求模組取得其操作物件。
例如要使用系統的http模組,可以這樣寫:
var libHttp = require('http'); //请求HTTP协议模块
這樣,以後的程式將可以透過變數libHttp存取http模組的功能。本章例程中使用了以下系統模組:
http:封裝http協定的伺服器與用戶端實作;
url:封裝對url的解析與處理;
fs:封裝對檔案系統操作的功能;
path:封裝對路徑的解析功能。
有了這些模組,我們就可以站在巨人的肩膀上建造自己的應用。
2、控制台
為了更好的觀察程式的運行,方便在異常時查看錯誤,可以通便變數console使用控制台的功能。
console.log('这是一段日志信息'); 计时并在控制台上输出计时信息: //开始计时 console.timeEnd('计时器1'); //开始名称为“计时器1”的计时器 ... ... ... //结束计时,并输出到控制台 console.timeEnd('计时器1'); //结束称为“计时器1”的计时器并输出
3、定義函數
在Node.js中定義函數的辦法與普通JavaScript中完全相同,不過我們推薦的寫法如下,即使用一個變數為函數命名,這樣可以比較方便明確的將函數作為參數傳遞給其他函數:
//定义一个名为showErr的函数 var showErr=function(msg){ var inf="错误!"+msg; console.log(inf+msg); return msg; }
4、建立Web伺服器並偵聽存取請求
建立Web伺服器最重要的是提供Web請求的回應函數,它有兩個參數,第一個代表客戶端請求的訊息,另一個代表將要傳回給客戶端的訊息。在回應函數中應解析請求訊息,依據請求,組裝返後內容。
//请求模块 var libHttp = require('http'); //HTTP协议模块 //Web服务器主函数,解析请求,返回Web内容 var funWebSvr = function (req, res){ res.writeHead(200, {'Content-Type': 'text/html'}); res.write('<html><body>'); res.write('<h1>*** Node.js ***</h1>'); res.write('<h2>Hello!</h2>'); res.end('</body></html>'); } //创建一个http服务器 var webSvr=libHttp.createServer(funWebSvr); //开始侦听8124端口 webSvr.listen(8124);
5、解析Web請求
對於簡單的Web網頁存取請求,重要的資訊包含在請求資訊參數的url裡,我們可以使用url解析模組解析url中的存取路徑,並利用path模組,將存取路徑組裝為要存取的實際檔案路徑用於返回。
var reqUrl=req.url; //获取请求的url //向控制台输出请求的路径 console.log(reqUrl); //使用url解析模块获取url中的路径名 var pathName = libUrl.parse(reqUrl).pathname; //使用path模块获取路径名中的扩展名 if (libPath.extname(pathName)=="") { //如果路径没有扩展名 pathName+="/"; //指定访问目录 } if (pathName.charAt(pathName.length-1)=="/"){ //如果访问目录 pathName+="index.html"; //指定为默认网页 } //使用路径解析模块,组装实际文件路径 var filePath = libPath.join("./WebRoot",pathName);
6、設定回傳頭
由於是Web請求,需要在回傳內容中包含http回傳頭,這裡重點是依據要存取的檔案路徑的檔案副檔名,設定http回傳頭的內容類型。
var contentType=""; //使用路径解析模块获取文件扩展名 var ext=libPath.extname(filePath); switch(ext){ case ".html": contentType= "text/html"; break; case ".js": contentType="text/javascript"; break; ... ... default: contentType="application/octet-stream"; } //在返回头中写入内容类型 res.writeHead(200, {"Content-Type": contentType });
7、寫入回傳物件中寫入存取的檔案內容
有了需要存取的檔案實際路徑,有了檔案對應的內容類型,就可以利用fs檔案系統模組讀取檔案流並傳回給客戶端。
//判断文件是否存在 libPath.exists(filePath,function(exists){ if(exists){//文件存在 //在返回头中写入内容类型 res.writeHead(200, {"Content-Type": funGetContentType(filePath) }); //创建只读流用于返回 var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null}); //指定如果流读取错误,返回404错误 stream.on("error", function() { res.writeHead(404); res.end("<h1>404 Read Error</h1>"); }); //连接文件流和http返回流的管道,用于返回实际Web内容 stream.pipe(res); } else { //文件不存在 //返回404错误 res.writeHead(404, {"Content-Type": "text/html"}); res.end("<h1>404 Not Found</h1>"); } });
二、測試及運行
1、完整原始碼
以下100行左右的JavaScript就是建立這樣一個簡單web伺服器的全部原始碼:
//------------------------------------------------ //WebSvr.js // 一个演示Web服务器 //------------------------------------------------ //开始服务启动计时器 console.time('[WebSvr][Start]'); //请求模块 var libHttp = require('http'); //HTTP协议模块 var libUrl=require('url'); //URL解析模块 var libFs = require("fs"); //文件系统模块 var libPath = require("path"); //路径解析模块 //依据路径获取返回内容类型字符串,用于http返回头 var funGetContentType=function(filePath){ var contentType=""; //使用路径解析模块获取文件扩展名 var ext=libPath.extname(filePath); switch(ext){ case ".html": contentType= "text/html"; break; case ".js": contentType="text/javascript"; break; case ".css": contentType="text/css"; break; case ".gif": contentType="image/gif"; break; case ".jpg": contentType="image/jpeg"; break; case ".png": contentType="image/png"; break; case ".ico": contentType="image/icon"; break; default: contentType="application/octet-stream"; } return contentType; //返回内容类型字符串 } //Web服务器主函数,解析请求,返回Web内容 var funWebSvr = function (req, res){ var reqUrl=req.url; //获取请求的url //向控制台输出请求的路径 console.log(reqUrl); //使用url解析模块获取url中的路径名 var pathName = libUrl.parse(reqUrl).pathname; if (libPath.extname(pathName)=="") { //如果路径没有扩展名 pathName+="/"; //指定访问目录 } if (pathName.charAt(pathName.length-1)=="/"){ //如果访问目录 pathName+="index.html"; //指定为默认网页 } //使用路径解析模块,组装实际文件路径 var filePath = libPath.join("./WebRoot",pathName); //判断文件是否存在 libPath.exists(filePath,function(exists){ if(exists){//文件存在 //在返回头中写入内容类型 res.writeHead(200, {"Content-Type": funGetContentType(filePath) }); //创建只读流用于返回 var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null}); //指定如果流读取错误,返回404错误 stream.on("error", function() { res.writeHead(404); res.end("<h1>404 Read Error</h1>"); }); //连接文件流和http返回流的管道,用于返回实际Web内容 stream.pipe(res); } else { //文件不存在 //返回404错误 res.writeHead(404, {"Content-Type": "text/html"}); res.end("<h1>404 Not Found</h1>"); } }); } //创建一个http服务器 var webSvr=libHttp.createServer(funWebSvr); //指定服务器错误事件响应 webSvr.on("error", function(error) { console.log(error); //在控制台中输出错误信息 }); //开始侦听8124端口 webSvr.listen(8124,function(){ //向控制台输出服务启动的信息 console.log('[WebSvr][Start] running at http://127.0.0.1:8124/'); //结束服务启动计时器并输出 console.timeEnd('[WebSvr][Start]'); });
2、資源目錄
WebRoot目錄來存放實際的網頁和圖片資源,「WebRoot」的目錄名稱在上述原始碼中被用來組裝實際檔案路徑。
3、運行並測試
在命令列中輸入:
node.exe WebSvr.js
我們的WebWebSvr.js
我們的WebWeb運作效果如下:
後記 利用Node.js我們可以方便建立相對獨立的Web伺服器,其事件驅動的特性避免繁瑣的執行緒開發,其基礎模組更降低了困難度。本章建立的Web伺服器只是一個簡單的樣本,沒有太多考慮的模組化、安全性等問題,但可以從中掌握Node.js開發的一些基本的知識。
更多Node.js實戰 建立簡單的Web伺服器相關文章請關注PHP中文網!