Node中的Connect——常用中介軟體
接下來介紹一個Connect內建的一些中介軟體:
static中介軟體
1.掛載
static允許將任意一個URL匹配到檔案系統中任意一個目錄。如:將/my-images URL和名為/images的目錄對應起來,可以以如下方式進行掛載:
server.use('/my-images', connect.static('/path/to/images'));
2. maxAge
static中介軟體接收一個名為maxAge的選項,這個選項代表一個資源在客戶端快取的時間。對於一些不經常改動的資源來說,我們可以進行快取,瀏覽器就無需每次都去請求它了。
server.use ('/js', connect.static('/path/to/bundles', {maxAge: 1000000000000}));
比如,一種Web應用常見的實踐方式就是將所有的客戶端JavaScript檔案都合併到一個檔案中,並在檔名中加上修訂號。這個時候,就可以設定maxAge選項,讓其永遠快取起來。如上述程式碼。
3. hidden
static接收的另一引數hidden。當hidden值為true時,Connect會託管那些檔名以點(.)開始的在UNIX檔案系統中被認為是隱藏的檔案:
server.use(connect.static('/path/to/resources', {hidden: true }));
query中介軟體
使用query中介軟體,能夠通過req.query物件自動獲取URL中的查詢字串。如:我們請求url /blog?page=5時,我們獲取資料查詢字串 page=5,可以這麼使用(req.url變數中儲存著URL的值,即 /blog?page=5):
server.use(connect.query);
server.use(function(req, res) {
// req.query.page == "5"
});
logger中介軟體
logger中介軟體將傳送進來的請求資訊和傳送出去的響應資訊列印在終端。
它提供了以下四種日誌格式:
- default
- dev
- short
- tiny
如:使用dev日誌格式,可以通過如下初始化logger中間的方式:
server.use(connect.logger('dev'));
dev是一種精準簡短的日誌格式,能夠提供行為以及效能方面的資訊,方便測試Web應用。
logger中間還允許自定義日誌輸出格式,還能通過動態的req和res來記錄頭資訊。
下面是完整的可用token:
- :req[header](如:req[accept])
- :res[header](如:res[content-length])
- :http-version
- :response-time
- :remote-addr
- :date
- :method
- :url
- :referrer
- :user-agent
- :status
使用如下:
server.use(connect.logger(':method :remote-addr'));
server.use(connect.logger('type is :res[content-type], length is '
+ ':res[content-length] and it took :response-time ms.'));
logger還能夠自定義token。如,要給請求Content-Type定義一個簡寫的:type token,可以採用如下方式:
connect.logger.token('type', function(req, res) {
return req.headers['content-type'];
});
body parser中介軟體
1.接收POST資料
使用body parse中介軟體可以接收POST請求的資料,並將資料儲存到req.body中。如:
server.use(connect.bodyParser());
server.use(function(req, res) {
// req.body.myinput
});
2.處理上傳
bodyParser另一功能就是使用formidable模組,它可以讓你處理使用者上傳的檔案。如:
var server = connect(
connect.bodyParser(),
connect.static('static')
);
server.use(function(req, res, next) {
if('POST' == req.method && req.body.file) {
console.log(req.body.file);
fs.readFile(req.body.file.path, 'utf8', function(err, data) {
if(err) {
res.writeHead(500);
res.end('Error!');
return;
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end([
'<h3>File: ' + req.body.file.name + '</h3>',
'<h4>Type: ' + req.body.file.type + '</h4>',
'<h4>Contents:</h4><pre>' + data + '</pre>'
].join(''));
});
} else {
next();
}
});
3.多檔案上傳
在input的name屬性上加上[],即
<input type="file" name="files[]"/>
<input type="file" name="files[]"/>
這個時候為多檔案上傳,這時req.body.files就包含了一個數組。
cookie
當瀏覽器傳送cookie資料時,會將其寫到cookie頭資訊中。其資料格式和URL中的查詢字串類似。如:
GET /detail HTTP/1.1
Host: 127.0.0.1
Cookie: key1=value1; key2=value2
Accept: */*
使用cookieParser中介軟體就可以通過req.cookies物件輕鬆訪問到這些cookie資料:
server.use(connect.cookieParser());
server.use(function(req, res, next) {
// req.cookies.key1 = "value1";
// req.cookies.key2 = "value2";
});
會話(session)
在絕大多數Web應用中,多個請求間共享“使用者會話”的概念是非常必要的。它主要通過在瀏覽器中設定cookie來實現,該cookie資訊會在隨後所有的請求頭資訊中被帶回到伺服器中。
使用者登入的案例:
var connect = require('connect'),
users = require('./users'); // 這裡直接require了JSON檔案,不需要使用module.exports對資料進行暴露
var server = connect(
connect.logger('dev'),
connect.bodyParser(),
connect.cookieParser(),
// 出於安全考慮,在初始化session中介軟體的時候需要提供secret選項
connect.session({secret: 'my app secret'}),
function(req, res, next) {
if('/' == req.url && req.session.logged_in) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('Welcome back, <b>' + req.session.name + '</b>.'
+ '<a href="/logout">Logout</a>');
} else {
next();
}
},
function(req, res, next) {
if('/' == req.url && 'GET' == req.method) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end([
'<form action="/login" method="POST">',
'<fieldset>',
'<legend>Please log in</legend>',
'<p>User: <input type="text" name="user"></p>',
'<p>Password: <input type="password" name="password"></p>',
'<button>Submit</button>',
'</fieldset>',
'</form>'
].join(''));
} else {
next();
}
},
function(req, res, next) {
if('/login' == req.url && 'POST' == req.method) {
res.writeHead(200);
if(!users[req.body.user] || req.body.password != users[req.body.user].password) {
res.end('Bad username/password');
} else {
req.session.logged_in = true;
req.session.name = users[req.body.user].name;
res.end('Authenticated!');
}
} else {
next();
}
},
function(req, res, next) {
if('/logout' == req.url) {
req.session.logged_in = false;
res.writeHead(200);
res.end('Logged out!');
} else {
next();
}
}
);
server.listen(3000);
其中users.json檔案內容如下:
{
"DreamBoy": {
"password": "123",
"name": "夢小白"
}
}
為了讓session能夠在生產環境中也正常工作,我們需要通過Redis來實現一個持久化。
Redis session
當我們登入成功後,重啟node伺服器,然後重新整理瀏覽器,會發現需要重新登入,也就是說session失效或或者說不見了。
原因在於session預設儲存在記憶體中。這意味著session資料儲存在記憶體中時,當程序退出後,session資料自然也就丟失了。
生產環境中,需要使用一種當應用重啟後,還能夠將session資訊持久化儲存下來的機制,如Redis。
Redis是一個既小又快的資料庫,有一個connect-redis模組使用Redis來持久化session資料,這樣就讓session駐紮到了Node程序之外。
使用如下(必須要安裝好Redis):
var connect = require('connect'),
RedisStore = require('connect-redis')(connect);
使用中介軟體:
server.use(connect.session({store: new RedisStore, secret: 'my secret'}));
methodOverrid中介軟體
basicAuth中介軟體
注:暫不說明。
總結
- 中介軟體是序列執行的。
- 使用中介軟體的好處:程式碼能以此為構建單元進行組織,並且能夠獲得高複用性。
- Connect是實現了中介軟體這一思路的模組,它為構建更具表達力的中介軟體提供了基礎架構。