Node.js實(shí)戰(zhàn) 建立簡(jiǎn)單的Web服務(wù)器
如果你熟悉.NET或其他類似平臺(tái)的Web開(kāi)發(fā),你可能會(huì)像,建立一個(gè)Web服務(wù)器有什么,在Visual Studio中建立一個(gè)Web工程,點(diǎn)擊運(yùn)行即可。事實(shí)的確是這樣,但請(qǐng)不要忘記,這樣的代價(jià)是,比如果說(shuō),你是用.NET開(kāi)發(fā)Web應(yīng)用,你就使用了完整的IIS作為你的Web服務(wù)器基礎(chǔ),這樣當(dāng)你的應(yīng)用發(fā)布時(shí)就只能用IIS了。而如果使用獨(dú)立服務(wù)器(利用System.Web.Hosting自己構(gòu)建的話),你則必須處理各種HttpListener和相應(yīng)線程,則會(huì)比較麻煩,畢竟.NET不是專注于面向Web的。Node.js在這方面提供了方便且可定制的途徑,你可以在其基礎(chǔ)上構(gòu)建精巧且完全面向你應(yīng)用的服務(wù)平臺(tái)。
一、建立簡(jiǎn)單的Web服務(wù)器涉及到Node.js的一些基本知識(shí)點(diǎn):
1、請(qǐng)求模塊
在Node.js中,系統(tǒng)提供了許多有用的模塊(當(dāng)然你也可以用JavaScript編寫自己的模塊,以后的章節(jié)我們將詳細(xì)講解),如http、url等。模塊封裝特定的功能,提供相應(yīng)的方法或?qū)傩?,要使用這些模塊,需要先請(qǐng)求模塊獲得其操作對(duì)象。
例如要使用系統(tǒng)的http模塊,可以這樣寫:
var libHttp = require('http'); //請(qǐng)求HTTP協(xié)議模塊
這樣,以后的程序?qū)⒖梢酝ㄟ^(guò)變量libHttp訪問(wèn)http模塊的功能。本章例程中使用了以下系統(tǒng)模塊:
http:封裝http協(xié)議的服務(wù)器和客戶端實(shí)現(xiàn);
url:封裝對(duì)url的解析和處理;
fs:封裝對(duì)文件系統(tǒng)操作的功能;
path:封裝對(duì)路徑的解析功能。
有了這些模塊,我們就可以站在巨人的肩膀上構(gòu)建自己的應(yīng)用。
2、控制臺(tái)
為了更好的觀察程序的運(yùn)行,方便在異常時(shí)查看錯(cuò)誤,可以通便變量console使用控制臺(tái)的功能。
console.log('這是一段日志信息');
計(jì)時(shí)并在控制臺(tái)上輸出計(jì)時(shí)信息:
//開(kāi)始計(jì)時(shí)
console.timeEnd('計(jì)時(shí)器1'); //開(kāi)始名稱為“計(jì)時(shí)器1”的計(jì)時(shí)器
...
...
...
//結(jié)束計(jì)時(shí),并輸出到控制臺(tái)
console.timeEnd('計(jì)時(shí)器1'); //結(jié)束稱為“計(jì)時(shí)器1”的計(jì)時(shí)器并輸出
3、定義函數(shù)
在Node.js中定義函數(shù)的辦法與普通JavaScript中完全相同,不過(guò)我們推薦的寫法如下,即使用一個(gè)變量為函數(shù)命名,這樣可以比較方便明確的將函數(shù)作為參數(shù)傳遞給其他函數(shù):
//定義一個(gè)名為showErr的函數(shù)
var showErr=function(msg){
var inf="錯(cuò)誤!"+msg;
console.log(inf+msg);
return msg;
}
4、創(chuàng)建Web服務(wù)器并偵聽(tīng)訪問(wèn)請(qǐng)求
創(chuàng)建Web服務(wù)器最重要的是提供Web請(qǐng)求的響應(yīng)函數(shù),它有兩個(gè)參數(shù),第一個(gè)代表客戶端請(qǐng)求的信息,另一個(gè)代表將要返回給客戶端的信息。在響應(yīng)函數(shù)中應(yīng)解析請(qǐng)求信息,依據(jù)請(qǐng)求,組裝返后內(nèi)容。
//請(qǐng)求模塊
var libHttp = require('http'); //HTTP協(xié)議模塊
//Web服務(wù)器主函數(shù),解析請(qǐng)求,返回Web內(nèi)容
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>');
}
//創(chuàng)建一個(gè)http服務(wù)器
var webSvr=libHttp.createServer(funWebSvr);
//開(kāi)始偵聽(tīng)8124端口
webSvr.listen(8124);
5、解析Web請(qǐng)求
對(duì)于簡(jiǎn)單的Web網(wǎng)頁(yè)訪問(wèn)請(qǐng)求,重要的信息包含在請(qǐng)求信息參數(shù)的url里,我們可以使用url解析模塊解析url中的訪問(wèn)路徑,并利用path模塊,將訪問(wèn)路徑組裝為要訪問(wèn)的實(shí)際文件路徑用于返回。
var reqUrl=req.url; //獲取請(qǐng)求的url
//向控制臺(tái)輸出請(qǐng)求的路徑
console.log(reqUrl);
//使用url解析模塊獲取url中的路徑名
var pathName = libUrl.parse(reqUrl).pathname;
//使用path模塊獲取路徑名中的擴(kuò)展名
if (libPath.extname(pathName)=="") {
//如果路徑?jīng)]有擴(kuò)展名
pathName+="/"; //指定訪問(wèn)目錄
}
if (pathName.charAt(pathName.length-1)=="/"){
//如果訪問(wèn)目錄
pathName+="index.html"; //指定為默認(rèn)網(wǎng)頁(yè)
}
//使用路徑解析模塊,組裝實(shí)際文件路徑
var filePath = libPath.join("./WebRoot",pathName);
6、設(shè)置返回頭
由于是Web請(qǐng)求,需要在返回內(nèi)容中包含http返回頭,這里重點(diǎn)是依據(jù)要訪問(wèn)的文件路徑的文件擴(kuò)展名,設(shè)置http返回頭的內(nèi)容類型。
var contentType="";
//使用路徑解析模塊獲取文件擴(kuò)展名
var ext=libPath.extname(filePath);
switch(ext){
case ".html":
contentType= "text/html";
break;
case ".js":
contentType="text/javascript";
break;
...
...
default:
contentType="application/octet-stream";
}
//在返回頭中寫入內(nèi)容類型
res.writeHead(200, {"Content-Type": contentType });
7、向返回對(duì)象中寫入訪問(wèn)的文件內(nèi)容
有了需要訪問(wèn)的文件實(shí)際路徑,有了文件對(duì)應(yīng)的內(nèi)容類型,就可以利用fs文件系統(tǒng)模塊讀取文件流并返回給客戶端。
//判斷文件是否存在
libPath.exists(filePath,function(exists){
if(exists){//文件存在
//在返回頭中寫入內(nèi)容類型
res.writeHead(200, {"Content-Type": funGetContentType(filePath) });
//創(chuàng)建只讀流用于返回
var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null});
//指定如果流讀取錯(cuò)誤,返回404錯(cuò)誤
stream.on("error", function() {
res.writeHead(404);
res.end("<h1>404 Read Error</h1>");
});
//連接文件流和http返回流的管道,用于返回實(shí)際Web內(nèi)容
stream.pipe(res);
}
else { //文件不存在
//返回404錯(cuò)誤
res.writeHead(404, {"Content-Type": "text/html"});
res.end("<h1>404 Not Found</h1>");
}
});
二、測(cè)試及運(yùn)行
1、完整源碼
以下100行左右的JavaScript就是建立這樣一個(gè)簡(jiǎn)單web服務(wù)器的全部源碼:
//------------------------------------------------
//WebSvr.js
// 一個(gè)演示W(wǎng)eb服務(wù)器
//------------------------------------------------
//開(kāi)始服務(wù)啟動(dòng)計(jì)時(shí)器
console.time('[WebSvr][Start]');
//請(qǐng)求模塊
var libHttp = require('http'); //HTTP協(xié)議模塊
var libUrl=require('url'); //URL解析模塊
var libFs = require("fs"); //文件系統(tǒng)模塊
var libPath = require("path"); //路徑解析模塊
//依據(jù)路徑獲取返回內(nèi)容類型字符串,用于http返回頭
var funGetContentType=function(filePath){
var contentType="";
//使用路徑解析模塊獲取文件擴(kuò)展名
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; //返回內(nèi)容類型字符串
}
//Web服務(wù)器主函數(shù),解析請(qǐng)求,返回Web內(nèi)容
var funWebSvr = function (req, res){
var reqUrl=req.url; //獲取請(qǐng)求的url
//向控制臺(tái)輸出請(qǐng)求的路徑
console.log(reqUrl);
//使用url解析模塊獲取url中的路徑名
var pathName = libUrl.parse(reqUrl).pathname;
if (libPath.extname(pathName)=="") {
//如果路徑?jīng)]有擴(kuò)展名
pathName+="/"; //指定訪問(wèn)目錄
}
if (pathName.charAt(pathName.length-1)=="/"){
//如果訪問(wèn)目錄
pathName+="index.html"; //指定為默認(rèn)網(wǎng)頁(yè)
}
//使用路徑解析模塊,組裝實(shí)際文件路徑
var filePath = libPath.join("./WebRoot",pathName);
//判斷文件是否存在
libPath.exists(filePath,function(exists){
if(exists){//文件存在
//在返回頭中寫入內(nèi)容類型
res.writeHead(200, {"Content-Type": funGetContentType(filePath) });
//創(chuàng)建只讀流用于返回
var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null});
//指定如果流讀取錯(cuò)誤,返回404錯(cuò)誤
stream.on("error", function() {
res.writeHead(404);
res.end("<h1>404 Read Error</h1>");
});
//連接文件流和http返回流的管道,用于返回實(shí)際Web內(nèi)容
stream.pipe(res);
}
else { //文件不存在
//返回404錯(cuò)誤
res.writeHead(404, {"Content-Type": "text/html"});
res.end("<h1>404 Not Found</h1>");
}
});
}
//創(chuàng)建一個(gè)http服務(wù)器
var webSvr=libHttp.createServer(funWebSvr);
//指定服務(wù)器錯(cuò)誤事件響應(yīng)
webSvr.on("error", function(error) {
console.log(error); //在控制臺(tái)中輸出錯(cuò)誤信息
});
//開(kāi)始偵聽(tīng)8124端口
webSvr.listen(8124,function(){
//向控制臺(tái)輸出服務(wù)啟動(dòng)的信息
console.log('[WebSvr][Start] running at http://127.0.0.1:8124/');
//結(jié)束服務(wù)啟動(dòng)計(jì)時(shí)器并輸出
console.timeEnd('[WebSvr][Start]');
});
2、資源目錄
既然要建立Web服務(wù)器,我們需要?jiǎng)?chuàng)建一個(gè)WebRoot目錄來(lái)存放實(shí)際的網(wǎng)頁(yè)和圖片資源,“WebRoot”的目錄名在以上源碼中被用于組裝實(shí)際文件路徑。
3、運(yùn)行并測(cè)試
在命令行中輸入:
node.exe WebSvr.js
我們的Web服務(wù)器就運(yùn)行起來(lái)了,這時(shí),可以通過(guò)瀏覽器對(duì)其進(jìn)行訪問(wèn),運(yùn)行效果如下:
后記
利用Node.js我們可以方便建立相對(duì)獨(dú)立的Web服務(wù)器,其事件驅(qū)動(dòng)的特性避免繁瑣的線程保護(hù),其基礎(chǔ)模塊更降低了開(kāi)發(fā)難度。本章建立的Web服務(wù)器只是一個(gè)簡(jiǎn)單的樣本,沒(méi)有過(guò)多的考慮模塊化、安全性等問(wèn)題,但可以從中掌握Node.js開(kāi)發(fā)的一些基本的知識(shí)。
作者:汪峰 www.otlive.cn
相關(guān)文章
微信小程序--onShareAppMessage分享參數(shù)用處(頁(yè)面分享)
本篇文章主要介紹了微信小程序的頁(yè)面分享onShareAppMessage分享參數(shù)用處的相關(guān)資料。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04原生JS 實(shí)現(xiàn)的input輸入時(shí)表格過(guò)濾操作示例
這篇文章主要介紹了原生JS 實(shí)現(xiàn)的input輸入時(shí)表格過(guò)濾操作,結(jié)合實(shí)例形式分析了JavaScript基于頁(yè)面元素遍歷、運(yùn)算、判斷實(shí)現(xiàn)的表格過(guò)濾相關(guān)操作技巧,需要的朋友可以參考下2019-08-08JavaScript實(shí)現(xiàn)表格動(dòng)態(tài)變色
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)表格動(dòng)態(tài)變色,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09js將table的每個(gè)td的內(nèi)容自動(dòng)賦值給其title屬性的方法
下面小編就為大家?guī)?lái)一篇js將table的每個(gè)td的內(nèi)容自動(dòng)賦值給其title屬性的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10javascript中setAttribute兼容性用法分析
這篇文章主要介紹了javascript中setAttribute兼容性用法,結(jié)合實(shí)例形式分析了javascript使用setAttribute進(jìn)行屬性設(shè)置操作的相關(guān)使用技巧,需要的朋友可以參考下2016-12-12