node新篇章:基於Express框架的HTTP服務器

閒來無事,不如擼碼

Express現在異常流行,尤其是近兩年。筆者現在上網找資料能看到好多“express實現…”、“基於express的…”實戰型文章。作爲一個簡潔而靈活的node.js Web應用框架,express提供了一系列強大特性幫助創建各種web應用。
express能做的事情有很多。。。


Express與Connect

說到express就不得不提到connect,其一是因爲它們出自於同一個作者,另一個原因是express中利用了connect中間件框架,整合了一些適用於web應用中需要的東西,比如:

  • 強大的路由
  • 視圖系統
  • 基於環境的配置
  • 持久會話
  • 內容協商
  • 響應工具
  • 重定向工具
  • 能快速生成應用骨架的功能

如何安裝express

首先,在cmd(或power shell)中切換到指定目錄;
然後,運行命令

npm install -g express

如果安裝的是express4以後的版本,則使用命令:npm install -g express-generator

(-generator安裝的是express生成器,express生成器和express不是一個概念 —— 雖然在express4以前兩者被統一在一起,但從4版本開始,兩者分開了,我們安裝的通常是生成器


用express搭建簡單web應用

如果前面用的是npm install -g express-generator命令,安裝完成後,鍵入命令:

express -e mxcyun

就會創建一個名爲mxcyun的web應用項目的文件骨架,但這裏還未完工 —— 一些項目依賴的組件還沒有安裝完成。
這時,我們還需cd到mxcyun目錄中,進行命令:

npm install

至此,項目搭建完成,我們可通過命令:

npm start

來啓動項目(在mxcyun目錄下)


express創建的默認文件結構

文件/目錄 說明
package.json npm依賴配置文件,類似ruby中的Gemfile、java Maven中的pom.xml
app.js 項目的入口文件,類似index.php
/public/ 靜態文件目錄
/views/ 視圖(模板)程序文件目錄
/routes/ 路由控制器程序文件目錄

express的程序控制

express提供了一系列用於搭建web應用程序的API:
cxkz

模板引擎ejs:
打開mxcyun項目根目錄的app.js文件,我們可以看到它簡潔的代碼。
其中app.set('views engine','ejs');就是設置默認的模板解析引擎。傳統開發語言,如ASP、PHP、JSP等,都沒有附帶模板解析功能,很多都是由第三方開發;而express不僅內置了模板解析,還提供了多種模板引擎,這是很多網頁開發語言無法媲美的優勢。
express默認使用ejs後綴的模板文件,模板目錄在views文件夾下。一般來說,網頁設計師或前端工程師輸出的文件基本上都是html後綴格式的文件,爲了減少轉換,只需配置一下即可讓express識別html格式的模板文件:

app.set('view engine','html');   //設置爲HTML後綴
app.engine('.html',require('ejs').__express);   //引擎依然爲ejs
app.set('view engine','z3f');   //設置爲自定義文件後綴
app.engine('.z3f',require('ejs').__express);

在這樣的情況下,前端模板渲染是根據服務器(後端)傳入的文字:

//routes.index -> /routes/index.js
var express=require('express');
var router=express.Router();

router.get('/',function(req,res,next){
	res.render('index',{title:'Express'})
});

module.exports=router;

這相當於數據,對應的模板文件爲/views/index.ejs,代碼如下:

<!DOCTYPE html>
<html>
	<head>
		<title><%= title %></title>
	</head>
	<body>
		<p>welcome to <%= title %></p>
	</body>
</html>

中間件middleware

中間件好像一直是業界推崇的一個點。中間件向流水線一樣,對來訪的請求進行層層處理,處理完後又交給下一環節,如此往復。
筆者曾見過老師寫的中間件,上面處理完請求下面處理報錯,然後處理數據,數據處理完再交給router,中間件封裝合理有序,整個過程異常好看(額…這裏用“好看”似乎不妥,權當做心情吧哈哈)。就像是java中的過濾器一樣。

在程序代碼中,哪些是中間件呢?
一個簡單的標識就是app.use(),通過這個方法調用的大多都是中間件。
比如:我們要禁止某些IP不能訪問本站,我們可以封裝這樣一箇中間件:

module.exports=function(req,res,next){
	var ips=['127.0.0.1','192.168.0.52'];
	if(ips.indexOf(req.connection.remoteAddress)>-1){
		res.send('stop ! ')
	}else{
		next();
	}
};

對了,中間件必有的一樣就是next() (因爲它只是整個流程中的“一環”),這也可以算作標識之一吧。

上面代碼邏輯很簡單:如果客戶端IP在“黑名單”(ips)裏,則通過res.end()種植程序運行,否則用next()繼續到下一個中間件!
在app.js中調用:

var bannedIP=require('./middleware/bannedIP');

//...
app.use(bannedIP);

express請求解析

路由routes
路由習慣上也被稱爲URL映射,基本上都是對URL的解析。
URL是跟隨http一起的,就像門牌號一樣重要,一個清晰的URL路徑甚至對於SEO優化也是非常重要的。
在express中主要用get、post、all這三種方法來處理路由的操作

express提供的路有格式主要有兩種:一是純字符串路由;二是正則表達式路由

app.get('/',routes.index);
app.get('/users',user.list);

app.get(/^\/page\/(\w+)(?:-(\w+))?$/,function(req,res){
	var from=req.params[0];
	var to=req.params[1] || from;
	console.log('page: '+from + '-'+to);
});

app.get('/:myvar/:myvar2',function(req,res){
	console.log('myvar: '+req.params.myvar+'<br />'myvar2: '+req.params.myvar2);
});

注意:每次修改完app.js必須重啓服務器

上面代碼中的req.params就是node中Request對象鎖提供的API,其餘還有比如:

方法或屬性 說明
req.params 數組對象,命名過的參數會以鍵值對的形式存放
req.query 一個解析過的請求參數對象
req.body 對應的是解析過的請求表單,由bodyParser()中間件提供(具體見這篇文章
req.files 上傳的文件對象,由bodyParser()中間件提供(具體見這篇文章
req.param(name) 返回name參數的值
req.route 當前匹配的路由裏包含的屬性
req.cookies Cookie對象,由cookieParser()中間件提供
req.is(type) 檢查頭文件裏是不是包含Content-Type字段
req.path 返回請求的URL的路徑名
req.host 返回從Host請求頭裏獲取的主機名,不包含端口號
req.fresh 判斷請求是不是新的
req.xhr 判斷請求頭裏是否有X-Requested-With這樣的字段並且值爲XMLHttpRequest
req.ip 返回客戶端地址

Response對象——express的響應控制,也有自己的API,常用的有:

方法或屬性 說明
res.status 返回指定響應狀態
res.cookie(name,value,[options]) 設置cookie name值爲value,也接受一個對象設置多個值
res.clearCookie(name,[options]) 清除指定cookie
res.redirect([status],url) 使用可選的狀態碼跳轉到url,狀態碼默認爲302-Found
res.charset 設置字符集,默認爲utf-8
res.send() 發送一個響應
res.json() 返回一個json響應
res.type(type) 設置Content-Type
res.sendfile(path,[options],[fn]) 傳輸指定path文件,根據擴展名自動設置響應頭
res.download(path,[filename],[fn]) 把path當做附件傳輸,通常瀏覽器會彈出一個下載文件的窗口,可回調
res.render(view,[locals],callback) 渲染view,同時向callback傳入渲染後的字符串

有關write、send、end輸出響應到客戶端

若是要服務器向客戶端發送信息——讓客戶端看到,就需要前面說的Response對象“大顯身手”了。
和大多數web編程語言一樣,write()方法幾乎是最常用的 —— 比如打印helloworld:

app.get('/hello',function(req,res){
	res.write('Helloworld');
	res.end('end');
});

end()方法也有這個功能,但是它最常用於“結束程序,開始顯示”,即到這裏時服務器會把所有前面準備好的響應信息發送給用戶和瀏覽器顯示。

和end()方法類似的還有send()方法 —— 他們一樣都能接收響應字符串,而他們和write()不同之處在於:他們返回的數據一般要由前端js繼續處理再顯示
而且,send()方法自動補足了一些響應頭信息,如ConnectionContent-TypeDate等等。

但是需要注意的是:在一個路由處理中,如果使用了send()方法,那麼在send()之前的代碼中出現write()方法則會報錯!
(當然,write()輸出的內容還是會發送到客戶端去,而且服務器端控制檯會報500錯,瀏覽器客戶端則依然是200狀態!)


設置cookie

對於一個網站來說,cookie是必不可少的,所以express也對cookie進行了封裝處理:
我們可以進行cookie的讀寫

//寫入一個cookie,設置100s後過期
res.cookie('user','z3f',{maxAge:100000});
//在控制檯打印cookie
console.log(req.cookie)

也可以使用cookies中間件處理json格式的cookie

app.use(express.cookieParser());


有關cookie的操作還有許多,日後再與大家討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章