node.js從入門到菜鳥——資源無法載入?你需要學會地址解析


node.js是什么似乎已經不需要我多為贅述了,非阻塞的服務器語言、JS書寫的后台代碼,無數的文章已經很好的展示了node的魅力與展望。關於node.js的安裝,大家不妨參考博客園聶微東的http://www.cnblogs.com/Darren_code/archive/2011/10/31/nodejs.html (node.js初體驗),這篇文章很好的綜述了node.js的一個基礎(從安裝到體驗到模塊的一個入門,個人感覺是一篇很好的文章),相信通過東哥的這篇文章大家可以對node有一個初步的了解。

node是一門很有意思的框架,它能夠讓一個長期執迷於前端開發的攻城濕(忘記了還有一種語言叫后端語言。。。)能夠覺得很舒適(編碼習慣都一樣),但是也同樣會讓一個新手覺得無所適從(為什么么有從入門到精通哩?參考書在哪里???)。這是一個前端高手為之一亮(也許只亮了一眼o(╯□╰)o),新手眼前一暈(if you want to find more information,please read the source(╯□╰)o)的框架。為了讓和我一樣的新手能夠多多少少摸到一點門路,我以身試法,來為新手找一條路~

本文旨在新手入門,所學尚淺,代碼水准有限,這也僅僅只是一個基本入門的筆記,高手可以笑一笑然后點關閉了。。。

首先我假設你已經安裝好了node(http://nodejs.org/#download,0.6.14已經很成熟了),那么首先我們來進行一個入門的編碼分析。

在進行分析之前,我們先來想一下以前的服務器端語言框架的工作原理。首先,用戶通過瀏覽器來訪問我們的應用。然后服務器通過對端口的監聽,來接收網絡端的request請求,並進行相應的處理。最后再將處理的結果返回客戶端。

好了,那么我們可以開始了。首先是服務器,要是沒有辦法啟動服務器,那么一切都是閑的。我們也許用過很多服務器端的語言(PHP、JAVA、ASP等等),接收HTTP請求並提供Web頁面的過程似乎不用我們來做,apache和IIS似乎都會幫我們來完成。但是在node里,這一步必須你自己做。我們來實現的不僅僅是一個應用,還需要實現HTTP處理的服務器。

好像很復雜的樣子,但對於node這並不是什么復雜的東西。我們即將進行一個HTTP服務器的初步學習,但是在學習之前我們需要溫習(預習?)一下node的模塊機制。

node采取的是模塊機制(和JS差不多),通過對模塊的導入我們可以聲明變量並將導入的模塊的實例化對象賦給變量並加以使用。具體各個模塊的使用方法以及用途請參考API,這里就不說了。。。

好了,我們回來看看HTTP服務器的構成。首先,我們先對HTTP模塊進行一次請求:

var http = require("http");

在node中require方法用於對各個模塊的引入,在這里我們需要對http模塊加以使用時就可以導入。導入后將之賦予http變量,然后對http變量進行操作。讓我們以hello world作為例子:

1 http.createServer(function(request, response) {
2 response.writeHead(200, {"Content-Type": "text/plain"});
3 response.write("Hello World");
4 response.end();
5 }).listen(12345,”127.0.0.1”);

每每看到hello world時內心都會有一絲變態的快意。。。

讓我們來回憶一下剛才的問題:實現一個http的服務器。其實這個很簡單,http模塊自帶的createServer方法就可以完成。該方法只有一個參數,類型為函數,在接口文檔中的定義是“始終接收request事件”。而server.listen方法(server就是剛才通過createServer創建的server)的參數是(port, [hostname], [callback]),第一個是監聽的端口號,第二個和第三個是可選項,第二個是主機名稱,第三個回調函數。這個函數只在綁定端口后進行調用。

接下來是回調函數,回調函數的參數有兩個,第一個是客戶端發送的request,第二個是服務器端的response。這段代碼進行了一個簡單的響應操作。首先是書寫了一個響應頭,響應頭參數包括狀態碼、原因描述以及頭部內容。狀態碼即http狀態碼,例如200、404、500之類的。原因描述與頭部內容可選,具體的就參見網絡報頭的書寫了,這里就不多說了(其實我也不會。。。)。writehead方法必須寫在end方法之前,這個是肯定的。。。

response的write方法就和JS的write所做的工作一樣,就是向頁面寫入數據,雖然原理不盡相同,但目前沒有准備去鑽研這部分源碼的我們可以忽略了。。。最后是end方法,它有兩個可選參數,分別是data與encoding。該方法用於所有的響應頭與響應正文輸出之后,進行響應的終結,並將管道流中的所有響應數據輸出。簡單地說就是在響應最后加上去的東西,它執行后會將響應執行。如果該方法帶有參數,那么就相當於先調用了response.write(data, encoding)方法,之后再調用無參數的end方法。

好了,最簡單的一個http服務器已經工作起來了。當用戶訪問127.0.0.1的12345端口時服務器會監聽到這一端口的request請求並書寫報頭與最簡單的helloworld於頁面上,用戶得到響應之后會在瀏覽器中顯示響應的內容,也就是helloworld。這個最簡單的服務器已經搭好了,但我們不能只滿足於這一點。

在繼續下一步的學習之前,我想給所有沒有使用過JS或者不怎么使用的同學大體的講述一下一個也許你們會略微奇怪的參數傳遞方法——函數傳遞。

在JS中,函數與數字、字符串等都是以var定義的,在參數傳遞的過程中所接受的參數也是var這種弱類型的。而function類型也是作為弱類型傳遞,當我們將一個函數進行傳遞時,所得到的不是該函數的返回值,而是這個函數本身。也就是說,這個函數在運行時會變成傳遞到的函數的本地變量(自己都覺得好亂。。。)。

讓我們回憶一下剛才的例子,在creatServer方法中我們使用了一個匿名函數作為參數,現在我們把這個匿名函數提出來:

 1 var http = require("http");
2 var serverhandel = function(request, response) {
3 response.writeHead(200, {"Content-Type": "text/plain"});
4 response.write("Hello World");
5 response.end();
6 }
7 function serverRequest (){
8 http.createServer(serverhandel).listen(12345);
9 }
10 exports. serverRequest = serverRequest;

exports即module.exports對象,在node中可以作為全局變量的賦予。也就是說它一般用來定義全局變量的,多用於模塊間的變量傳遞。在此我需要簡單說一下JS的模塊機制,JS中的模塊多用閉包進行包裹(我也不知道這么說對不對),而在閉包中定義的局部變量則無法在全局展開使用,也就是說別的地方調用這個模塊時不能將其中的局部變量單獨的進行使用。而exports則可以在載入模塊后將該函數載入全局變量的作用鏈中。

說到這大家也應該明白了,我們要進行一次模塊引用。將這段代碼存入serverRequest.js中,然后建立一個index.js文件,然后引用serverRequest模塊:

1 var server = require(“./serverRequest”);
2 server. serverRequest();

這樣我們就進行了一個最基本的小模塊的搭建,也初步的了解了一下node的模塊體系。那么下一步我們就要進行下連個個非常重要的模塊的學習,也就是url模塊與path模塊。

url模塊的作用是從請求中獲取請求的url並進行處理,它有着幾個常用的方法:

1 url.parse(string).pathname;
2 url.parse(string).query;

第一個方法的作用是獲取url請求部分的域名之后的路徑名稱,第二個方法獲取的則是通過get向服務器傳遞的參數。

而path模塊的作用是解決文件路徑問題,我們這次先學習三個方法:

1 path.extname(p);
2 path.join([path1], [path2], [...]);
3 path.exists(p, [callback]);

第一個方法是獲取擴展名的方法,參數是url路徑。第二個方法是做路徑拼接使用,用來標准化最終路徑,參數是需要拼接的路徑。第三個方法是檢驗路徑存在與否,第一個參數是標准化的路徑,第二個是可選的回調函數,無論路徑存在與否都會被調用,函數有一個exist參數,標示路徑是否存在。

好了,現在我們就可以通過這兩個模塊進行一個簡單的路徑服務器的搭建了。通過這個服務器的搭建,我們可以對本地的靜態網站進行部署,對於頁面以及網頁所需要載入的各種資源進行尋址,最后對請求的資源進行反饋。

 1 //請求模塊
2 var http = require('http');
3 var url=require('url');
4 var fs = require("fs"); //在這里先導入文件模塊,僅僅做一個簡單的操作,具體有關文件模塊的學習在之后的文件服務器上會進行進一步的學習。
5 var path = require("path");
6 //創建一個http服務器
7 var server=http.createServer(start).listen(12345);
8 //依據路徑獲取返回內容類型字符串,用於http返回頭
9 var getContentType=function(filePath){
10 var contentType="";
11 //使用路徑解析模塊獲取文件擴展名
12 var extension=path.extname(filePath);
13 switch(extension){
14 case ".html":
15 contentType= "text/html";
16 break;
17 case ".js":
18 contentType="text/javascript";
19 break;
20 case ".css":
21 contentType="text/css";
22 break;
23 case ".gif":
24 contentType="image/gif";
25 break;
26 case ".jpg":
27 contentType="image/jpeg";
28 break;
29 case ".png":
30 contentType="image/png";
31 break;
32 case ".ico":
33 contentType="image/icon";
34 break;
35 default:
36 contentType="application/octet-stream";
37 }
38 return contentType; //返回內容類型字符串
39 }
40 //Web服務器主函數,解析請求,返回Web內容
41 var funWebSvr = function (req, res){
42 //獲取請求的url
43 var url=req.url;
44 //使用url解析模塊獲取url中的路徑名
45 var pathName = url.parse(reqUrl).pathname;
46 if (path.extname(pathName)=="") {
47 //如果路徑沒有擴展名
48 if (pathName.length<2) {//如果是默認域名
49 pathName+="/";
50 }
51 else{
52 pathName+=".html";
53 }
54 }
55 else{
56 if (path.extname(pathName)!=".html"){
57 pathName=".."+ pathName;
58 }
59 }
60 if (pathName.charAt(pathName.length-1)=="/"){
61 //如果訪問目錄
62 pathName+="login.html"; //指定為默認網頁
63 }
64 var filePath = pathName;
65 //使用路徑解析模塊,組裝實際文件路徑
66 if (pathName.charAt(pathName.length).search(/./) == -1) {
67 filePath = libPath.join("./html",pathName);
68 };
69 //判斷文件是否存在
70 libPath.exists(filePath,function(exists){
71 if(exists){//文件存在
72 //在返回頭中寫入內容類型
73 res.writeHead(200, {"Content-Type": funGetContentType(filePath) });
74 //創建只讀流用於返回
75 var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null});
76 //指定如果流讀取錯誤,返回404錯誤
77 stream.on("error", function() {
78 res.writeHead(404);
79 res.end("<h1>404 Read Error</h1>");
80 });
81 //連接文件流和http返回流的管道,用於返回實際Web內容
82 stream.pipe(res);
83 }
84 else {//文件不存在
85 //返回404錯誤
86 res.writeHead(404, {"Content-Type": "text/html"});
87 res.end("<h1>404 Not Found</h1>");
88 }
89 });
90 }

這是當時對着一篇大牛的博文敲的例子,后來發現只能載入單個網頁,而其他資源不能很好的載入,就進行了一次較大的改正,主要添加了對不同pathname的尋址以及載入。本例的css、js以及image文件夾都與頁面所在的html文件夾在同一目錄下。

相信通過這個例子大家已經能簡單的讓一個靜態網站在我們的服務器上支持起來了。我們下一次將會簡單的部署一個文件系統,希望大家能繼續關注。新手上道,文章代碼寫的都比較粗糙,希望大家指正。bye





 








免責聲明!

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



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