http://www.tuicool.com/articles/emeuiejavascript
http://blog.fens.me/nodejs-connect/php
「中間件」在軟件領域是一個很是廣的概念,除操做系統的軟件均可以稱爲中間件,好比,消息中間件,ESB中間件,日誌中間件,數據庫中間件等等。css
Connect被定義爲Node平臺的中間件框架,從定位上看Connect必定是出衆的,普遍兼容的,穩定的,基礎的平臺性框架。若是攻克Connect,會有助於咱們更瞭解Node的世界。Express就是基於Connect開發的。html
讓咱們開始探索Connect中間件。java
Connect是一個node中間件(middleware)框架。若是把一個http處理過程比做是污水處理,中間件就像是一層層的過濾網。每一箇中間件在http處理過程當中經過改寫request或(和)response的數據、狀態,實現了特定的功能。這些功能很是普遍,下圖列出了connect全部內置中間件和部分第三方中間件。 這裏能看到 完整的中間件列表 。node
下圖根據中間件在整個http處理流程的位置,將中間件大體分爲3類:python
關於Connect介紹部分,摘自:http://www.cnblogs.com/luics/archive/2012/11/28/2775206.htmlgit
經過nodejs安裝Connect程序員
~ D:\workspace\javascript>mkdir nodejs-connect && cd nodejs-connect ~ D:\workspace\javascript\nodejs-connect> npm install connect connect@2.9.0 node_modules\connect ├── methods@0.0.1 ├── uid2@0.0.2 ├── pause@0.0.1 ├── cookie-signature@1.0.1 ├── fresh@0.2.0 ├── qs@0.6.5 ├── bytes@0.2.0 ├── buffer-crc32@0.2.1 ├── cookie@0.1.0 ├── debug@0.7.2 ├── send@0.1.4 (range-parser@0.0.4, mime@1.2.11) └── multiparty@2.1.8 (stream-counter@0.1.0, readable-stream@1.0.17)
增長一個文件:app.jsgithub
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(function (req, res) { res.end('hello world\n'); }) .listen(3000);
啓動node
~ D:\workspace\javascript\nodejs-connect>node app.js GET / 200 5ms GET /favicon.ico 200 0ms
打開瀏覽器:http://localhost:3000/
下面將分別介紹這22箇中間件。
描述:用來輸出用戶請求日誌。
參數:options或者format字符串
options:
tokens: format格式
Formats:縮寫
例子:新建logger.js
var connect = require('connect'); var app = connect() .use(connect.logger()) .use(function (req, res) { res.end('hello world\n'); }) .listen(3000);
connect.logger()
127.0.0.1 - - [Mon, 23 Sep 2013 05:14:18 GMT] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi t/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36" 127.0.0.1 - - [Mon, 23 Sep 2013 05:14:18 GMT] "GET /favicon.ico HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"
connect.logger(‘short’)
127.0.0.1 - GET / HTTP/1.1 200 - - 9 ms 127.0.0.1 - GET /favicon.ico HTTP/1.1 200 - - 1 ms
connect.logger(‘dev’)
GET / 200 5ms GET /favicon.ico 200 1ms
connect.logger(function(tokens, req, res){ return ‘some format string’ })
some format string some format string
因此在開發環境,咱們日誌設置成dev就行了。
描述:cookie解析中間件,解析Cookies的頭經過req.cookies獲得cookies。還能夠經過req.secret加密cookies。
例子:新建cookieParser.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.cookieParser('secret string')) .use(function (req, res, next) { req.cookies.website="blog.fens.me"; res.end(JSON.stringify(req.cookies)); }) .listen(3000);
描述:會話管理中間件
依賴:cookieParser
參數:options
options:
Cookie option:
cookie.maxAge: 默認值null,表示當瀏覽器關閉後cookie被刪除。
例子:新建session.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.cookieParser()) .use(connect.session({secret: '123', cookie: { maxAge: 60000 }})) .use(function (req, res, next) { if(req.session.pv){ res.setHeader('Content-Type', 'text/html'); res.write('views: ' + req.session.pv); req.session.pv++; res.end(); }else{ req.session.pv = 1; res.end('Refresh'); } }) .listen(3000);
描述:基於cookies的會話中間件
參數:options:
options:
Clearing sessions:
req.session = null;
例子:新建cookieSession.js
var connect = require('connect'); var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.cookieParser()) .use(connect.cookieSession({ secret: 'adddaa!', cookie: { maxAge: 60 * 60 * 1000 }})) .use(function (req, res, next) { if(req.session.pv){ res.setHeader('Content-Type', 'text/html'); res.write('views: ' + req.session.pv ); req.session.pv++; console.log(req.session.pv); res.end(); }else{ req.session.pv = 1; res.end('Refresh'); } }) .listen(3000);
咱們發現,此次無論刷新多少次頁面,req.session.pv始終是1.
描述:gzip壓縮中間件,經過filter函數設置,須要壓縮的文件類型。壓縮算法爲gzip/deflate。
filter函數
exports.filter = function(req, res){ return /json|text|javascript|dart|image\/svg\+xml|application\/x-font-ttf|application\/vnd\.ms-opentype|application\/vnd\.ms-fontobject/.test(res.getHeader('Content-Type')); };
Threshold壓縮閾值:當響應請求大於閾值,則壓縮響應請求。
參數:options
例子:新建compress.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.compress({level:9})) .use(function (req, res) { res.setHeader('Content-Type', 'text/html'); res.write(res); res.end('hello world\n'); }) .listen(3000);
描述:basic認證中間件,實現簡單的用戶密碼登錄,當用戶和密碼驗證經過後,經過一個callback方法繼續執行。
同步驗證:
connect() .use(connect.basicAuth(function(user, pass){ return 'tj' == user && 'wahoo' == pass; }))
異步驗證:
connect()
.use(connect.basicAuth(function(user, pass, fn){ User.authenticate({ user: user, pass: pass }, fn); }))
例子:新建basicAuth.js
var connect = require('connect'); var app = connect(); app.use(connect.logger('dev')); // 基本用法 // app.use(connect.basicAuth('fens','fens')) // 同步驗證 app.use(connect.basicAuth(function (user, pass) { var isLogin = 'fens' == user && 'fens' == pass; console.log("Login:" + isLogin); return isLogin; })) app.use(function (req, res) { res.end('hello world\n'); }) app.listen(3000);
驗證彈出框
正確輸入用戶和密碼後,訪問頁面
描述:請求內容解析中間件,支持多種類型application/json,
application/x-www-form-urlencoded, multipart/form-data.
與其餘的3箇中間件相同:
app.use(connect.json()); app.use(connect.urlencoded()); app.use(connect.multipart());
例子:新建bodyParser.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.bodyParser()) .use(function(req, res) { res.end('req.body=>' + JSON.stringify(req.body)); }) .listen(3000);
POST方法:
~ curl -d 'user[name]=tj' http://localhost:3000/ req.body=>{"'user":{"name":"tj'"}}
GET方法:
~ curl http://localhost:3000/?user=123 req.body=>{}
描述:JSON解析中間件,req.body傳值
參數:options
同bodyParser。
描述:application/x-www-form-urlencode請求解析中間件
參數:options
同bodyParser。
描述:multipart/form-data請求解析中間件,解析req.body和req.files.
上傳文件:uploadDir
app.use(connect.multipart({ uploadDir: path }))
參數:options
Temporary Files:臨時文件
默認狀況下,臨時文件會被保存在os.tmpDir()目錄,但不會自動迴歸,咱們必須手動處理。若是沒有使用defer選項時,你能夠經過req.files來得到對象的使用。
req.files.images.forEach(function(file){ console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path); });
Streaming:流式處理
當使用defer選項時,文件在上傳過程當中,你能夠經過」part」事件和流控制訪問文件。
req.form.on('part', function(part){ // transfer to s3 etc console.log('upload %s %s', part.name, part.filename); var out = fs.createWriteStream('/tmp/' + part.filename); part.pipe(out); }); req.form.on('close', function(){ res.end('uploaded!'); });
例子:新建multipart.js
var connect = require('connect');
var app = connect()
.use(connect.logger('dev'))
.use(connect.multipart({ uploadDir: 'd:\\tmp' }))
.use(function (req, res) {
if(req.method=='POST'){
console.log(req.files);
res.end('Upload ==>'+ req.files.file.path);
}
res.setHeader('Content-Type', 'text/html');
res.write('<form enctype="multipart/form-data" method="POST"><input type="file" name="file">'); res.write('<input type="submit" value="submit"/>'); res.write('</form>'); res.end('hello world\n'); }) .listen(3000);
經過form表單選擇文件
POST請求解析
描述:請求超時中間件,默認超時時間是5000ms,能夠清除這個時間經過req.clearTimeout()函數。超時的錯誤,能夠經過next()函數傳遞。咱們也能夠設置超時的響應錯誤狀態碼:.timeout=503
例子:新建timeout.js,模擬超時
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.timeout(1000)) .use(function (req, res) { setTimeout(function(){ res.end('hello world\n'); },5000) }) .listen(3000);
控制檯輸出:
Error: Response timeout
at IncomingMessage. (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.j s:39:17) at IncomingMessage.EventEmitter.emit (events.js:95:17) at null._onTimeout (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.js:34:11) at Timer.listOnTimeout [as ontimeout] (timers.js:110:15) GET / 503 1030ms - 389b Error: Response timeout at IncomingMessage. (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.j s:39:17) at IncomingMessage.EventEmitter.emit (events.js:95:17) at null._onTimeout (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\timeout.js:34:11) at Timer.listOnTimeout [as ontimeout] (timers.js:110:15) GET /favicon.ico 503 1006ms - 389b
描述:計算響應時間中間件,在response的header增長X-Response-Time,計算響應時間。
例子:新建reponseTime.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.responseTime()) .use(function (req, res) { res.end('hello world\n'); }) .listen(3000);
沒法模擬出效果,暫時先跳過
描述: 提供僞造HTTP中間件,檢查一個method是否被重寫,經過傳遞一個key,獲得_method,原始的方法經過req.originalMethod得到。
例子:新建methodOverride.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.methodOverride('KEY')) .use(function (req, res) { res.end(JSON.stringify(req.headers)); }) .listen(3000);
描述:跨域請求csrf保護中間件,經過req.csrfToken()令牌函數綁定到請求的表單字段。這個令牌會對訪客會話進行驗證。
默認狀況會檢查經過bodyParser()產生的req.body,query()函數產生的req.query,和X-CSRF-Token的header。
依賴:session(), cookieParser()
參數:options:
例子:新建csrf.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.cookieParser()) .use(connect.session({secret: '123', cookie: { maxAge: 60000 }})) .use(connect.csrf({value:'admin'})) .use(function (req, res) { res.setHeader('Content-Type', 'text/html'); res.end('hello world\n'); console.log(req.csrfToken()); }) .listen(3000);
生成的csrf Token:
1YZ72JuGRTOh/mzqByktPoyz2C+Dk1E5wXyj0= GET / 200 356ms bItSjAAXydK4jkYYLDnc1c0+7AGDFwGX6r8ns= GET /favicon.ico 200 3ms
描述: 靜態文件處理中間件,設置root路徑做爲靜態文件服務器
參數:options:
options:
例子:新建static.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.static(__dirname+'/static',{maxAge:60*60*1000,hidden:false})) .use(function (req, res) { res.setHeader('Content-Type', 'text/html'); res.write('static:'); res.write(''); res.write('hidden:'); res.end(''); }) .listen(3000);
隱藏類型的文件,沒有被顯示出來。
描述: 靜態文件緩存中間件,最大能夠緩存128個對象,每一個對象最大256K,總加起來32mb。
緩存算法採用LRU(最近最少使用)算法,讓最活躍的對象保存在緩存中,從而增長命中。
Benchmarks:性能測試
依賴:static()
參數:options:
例子:新建staticCache.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.static(__dirname+'/static',{maxAge:60*60*1000,hidden:false})) .use(connect.staticCache()) .use(function (req, res) { res.setHeader('Content-Type', 'text/html'); res.write('static:'); res.write('<img src="static.png" width="100px"/>'); res.write('hidden:'); res.end('<img src=".png" width="100px"/>'); }) .listen(3000);
控制檯日誌:
connect.staticCache() is deprecated and will be removed in 3.0 use varnish or similar reverse proxy caches. GET / 200 11ms GET /.png 200 0ms
建議用varnish或專門的緩存工具,來代替staticCache()。
描述: 目錄列表中間件,列出指定目錄下的文件
參數:options:
例子:新建directory.js
var connect = require('connect'); var app = connect() .use(connect.logger('dev')) .use(connect.directory(__dirname+'/static',{hidden:true})) .use(function (req, res) { res.end(); }) .listen(3000);
沒法模擬出效果,暫時先跳過
描述: 虛擬二級域名映射中間件,設置hostname和server。
例子:新建vhost.js
var connect = require('connect'); var app = connect(); app.use(connect.logger('dev')); app.use(function (req, res) { res.end(JSON.stringify(req.headers.host)); }); var fooApp = connect(); fooApp.use(connect.logger('dev')); fooApp.use(function (req, res) { res.end('hello fooApp\n'); }); var barApp = connect(); barApp.use(connect.logger('dev')); barApp.use(function (req, res) { res.end('hello barApp\n'); }); app.use(connect.vhost('foo.com', fooApp)); app.use(connect.vhost('bar.com', barApp)); app.listen(3000);
描述:網頁圖標中間件,指定favicon的路徑
參數:options:
例子:新建favicon.js
var connect = require('connect'); var app = connect() .use(connect.favicon('static/favicon.ico')) .use(connect.logger('dev')) .use(function (req, res) { res.end('hello world\n'); }) .listen(3000);
網站圖標
描述: 請求內容大小限制中間件,能夠用mb,kb,gb表示單位。
例子:新建limit.js
var connect = require('connect');
var app = connect()
.use(connect.logger('dev'))
.use(connect.limit('1mb'))
.use(connect.multipart({ uploadDir: 'd:\\tmp' }))
.use(function (req, res) {
if(req.method=='POST'){
console.log(req.files);
res.end('Upload ==>'+ req.files.file.path);
}
res.setHeader('Content-Type', 'text/html');
res.write('<form enctype="multipart/form-data" method="POST"><input type="file" name="file">'); res.write('<input type="submit" value="submit"/>'); res.write('</form>'); res.end('hello world\n'); }) .listen(3000);
控制檯日誌,上傳大於1mb的文件。
GET / 200 8ms Error: Request Entity Too Large at Object.exports.error (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\utils.js:62:13) at Object.limit [as handle] (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\limit.js:46: 47) at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15) at Object.logger (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\logger.js:156:5) at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15) at Function.app.handle (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:198:3) at Server.app (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\connect.js:65:37) at Server.EventEmitter.emit (events.js:98:17) at HTTPParser.parser.onIncoming (http.js:2027:12) at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:119:23) POST / 413 8ms - 961b
描述:URL解析中間件,自動解析URL查詢參數req.query
參數:options,qs.parse函數
例子:新建query.js
var connect = require('connect'); var app = connect() .use(connect.query()) .use(connect.logger('dev')) .use(function (req, res) { res.end(JSON.stringify(req.query)); }) .listen(3000);
經過CURL測試:
curl -d '{name:'aad'}' http://localhost:3000?pass=did {"pass":"did"}
req.query會自動解析URL的查詢參數,不解析POST的數據。
沒法模擬出效果,暫時先跳過
描述:錯誤處理中間件,對於開發過程當中的錯誤,提供棧跟蹤和錯誤響應,接受3種類型text,html,json。
Text: text/plain是默認類型,返回一個簡單的棧跟蹤和錯誤消息
JSON:application/json,返回{‘error’:error}對象
HTML: 返回一個HTML錯誤頁面
參數:options:
例子:新建errorHadnler.js
var connect = require('connect'); var app = connect() .use(connect.logger()) .use(connect.errorHandler({ dumpExceptions: true, showStack: true })) .use(function (req, res) { req.headers.accept='html'; res.write(JSON.stringify(req.headers.accept)); throw new Error('my errorHandler!!!'); res.end('Hello'); }) .listen(3000);
控制檯輸出
Error: my errorHandler!!! at Object.handle (D:\workspace\javascript\nodejs-connect\errorHadnler.js:8:15) at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15) at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:192:9) at Object.logger (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\middleware\logger.js:156:5) at next (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:190:15) at Function.app.handle (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\proto.js:198:3) at Server.app (D:\workspace\javascript\nodejs-connect\node_modules\connect\lib\connect.js:65:37) at Server.EventEmitter.emit (events.js:98:17) at HTTPParser.parser.onIncoming (http.js:2027:12) at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:119:23)