Nodejs基礎中間件Connect

http://www.tuicool.com/articles/emeuiejavascript

 

關於做者

  • 張丹(Conan), 程序員Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

轉載請註明出處:

http://blog.fens.me/nodejs-connect/php

前言

「中間件」在軟件領域是一個很是廣的概念,除操做系統的軟件均可以稱爲中間件,好比,消息中間件,ESB中間件,日誌中間件,數據庫中間件等等。css

Connect被定義爲Node平臺的中間件框架,從定位上看Connect必定是出衆的,普遍兼容的,穩定的,基礎的平臺性框架。若是攻克Connect,會有助於咱們更瞭解Node的世界。Express就是基於Connect開發的。html

讓咱們開始探索Connect中間件。java

目錄

  1. Connect介紹
  2. Connect安裝
  3. Connect內置中間件介紹
  4. logger
  5. cookieParser
  6. session
  7. cookieSession
  8. compress
  9. basicAuth
  10. bodyParser
  11. json
  12. urlencoded
  13. multipart
  14. timeout
  15. reponseTime
  16. methodOverride
  17. csrf
  18. static
  19. staticCache
  20. directory
  21. vhost
  22. favicon
  23. limit
  24. query
  25. errorHadnler

1. Connect介紹

Connect是一個node中間件(middleware)框架。若是把一個http處理過程比做是污水處理,中間件就像是一層層的過濾網。每一箇中間件在http處理過程當中經過改寫request或(和)response的數據、狀態,實現了特定的功能。這些功能很是普遍,下圖列出了connect全部內置中間件和部分第三方中間件。 這裏能看到 完整的中間件列表 。node

下圖根據中間件在整個http處理流程的位置,將中間件大體分爲3類:python

  • 1. Pre-Request 一般用來改寫request的原始數據
  • 2. Request/Response 大部分中間件都在這裏,功能各異
  • 3. Post-Response 全局異常處理,改寫response數據等

關於Connect介紹部分,摘自:http://www.cnblogs.com/luics/archive/2012/11/28/2775206.htmlgit

2. Connect安裝

個人系統環境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

經過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) 

嘗試作一個最簡單的web服務器

增長一個文件: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/

3. Connect內置中間件介紹

22個內置中間件列表

下面將分別介紹這22箇中間件。

4. logger

描述:用來輸出用戶請求日誌。

參數:options或者format字符串

options:

  • format:參考下面的tokens
  • stream:輸出流,默認是stdout
  • buffer:緩衝時間,默認是1000ms
  • immediate:馬上打印日誌

tokens: format格式

  • :req[header] ex: :req[Accept]
  • :res[header] ex: :res[Content-Length]
  • :http-version
  • :response-time
  • :remote-addr
  • :date
  • :method
  • :url
  • :referrer
  • :user-agent
  • :status

Formats:縮寫

  • default ‘:remote-addr – – [:date] 「:method :url HTTP/:http-version」 :status :res[content-length] 「:referrer」 「:user-agent」‘
  • short ‘:remote-addr – :method :url HTTP/:http-version :status :res[content-length] – :response-time ms’
  • tiny ‘:method :url :status :res[content-length] – :response-time ms’
  • dev concise output colored by response status for development use

例子:新建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就行了。

5. cookieParser

描述: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); 

6. session

描述:會話管理中間件

依賴:cookieParser

參數:options

options:

  • key:Cookies名,默認值爲connect.sid
  • store: session存儲實例
  • secret: session的cookie加密
  • cookie: session的cookie配置,默認值爲{path: ‘/’, httpOnly: true, maxAge: null}
  • proxy:安全cookie的反向代理,經過x-forwarded-proto實現

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); 

7. cookieSession

描述:基於cookies的會話中間件

參數:options:

options:

  • key:Cookies名,默認值爲connect.sess
  • secret: 防止cookie竊取
  • cookie: session的cookie配置,默認值爲{path: ‘/’, httpOnly: true, maxAge: null}
  • proxy:安全cookie的反向代理,經過x-forwarded-proto實現

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.

8. compress

描述: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

  • chunkSize (default: 16*1024)
  • windowBits
  • level: 0-9 where 0 is no compression, and 9 is slow but best compression
  • memLevel: 1-9 low is slower but uses less memory, high is fast but uses more
  • strategy: compression strategy

例子:新建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); 

9. basicAuth

描述: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); 

驗證彈出框

正確輸入用戶和密碼後,訪問頁面

10. bodyParser

描述:請求內容解析中間件,支持多種類型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=>{} 

11. json

描述:JSON解析中間件,req.body傳值

參數:options

  • strict: when false anything JSON.parse() accepts will be parsed
  • reviver: used as the second 「reviver」 argument for JSON.parse
  • limit: byte limit [1mb]

同bodyParser。

12. urlencoded

描述:application/x-www-form-urlencode請求解析中間件

參數:options

  • limit: byte limit [1mb]

同bodyParser。

13. multipart

描述:multipart/form-data請求解析中間件,解析req.body和req.files.

上傳文件:uploadDir

app.use(connect.multipart({ uploadDir: path })) 

參數:options

  • limit: byte limit defaulting to [100mb]
  • defer: 在不等待「結束」事件,經過調用req.form.next()函數,能夠緩衝並處理多個表單對象,還能夠綁定到「progress」或「events」的事件。

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請求解析

14. timeout

描述:請求超時中間件,默認超時時間是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 

15. reponseTime

描述:計算響應時間中間件,在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); 

16. methodOverride

沒法模擬出效果,暫時先跳過

描述: 提供僞造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); 

17. csrf

描述:跨域請求csrf保護中間件,經過req.csrfToken()令牌函數綁定到請求的表單字段。這個令牌會對訪客會話進行驗證。

默認狀況會檢查經過bodyParser()產生的req.body,query()函數產生的req.query,和X-CSRF-Token的header。

依賴:session(), cookieParser()

參數:options:

  • value: 一個函數接受請求,返回的令牌

例子:新建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

18. static

描述: 靜態文件處理中間件,設置root路徑做爲靜態文件服務器

參數:options:

options:

  • maxAge:瀏覽器緩存存活時間(毫秒),默認值0
  • hidden:是否容許傳遞隱藏類型的文件,默認值false
  • redirect:是否容許當訪問名是一個目錄,結尾增長」/」,默認值true
  • index:設置默認的文件名,默認值index.html

例子:新建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); 

隱藏類型的文件,沒有被顯示出來。

19. staticCache

描述: 靜態文件緩存中間件,最大能夠緩存128個對象,每一個對象最大256K,總加起來32mb。

緩存算法採用LRU(最近最少使用)算法,讓最活躍的對象保存在緩存中,從而增長命中。

Benchmarks:性能測試

  • static(): 2700 rps
  • node-static: 5300 rps
  • static() + staticCache(): 7500 rps

依賴:static()

參數:options:

  • maxObjects:最多能緩存的對象,默認值128個
  • maxLength:最大緩存的對象,默認值256kb

例子:新建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()。

20. directory

描述: 目錄列表中間件,列出指定目錄下的文件

參數:options:

  • hidden:是否顯示隱藏文件,默認值false.
  • icons:是否顯示網站圖標,默認值false.
  • filter:是否過濾文件,默認值false.

例子:新建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); 

21. vhost

沒法模擬出效果,暫時先跳過

描述: 虛擬二級域名映射中間件,設置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); 

22. favicon

描述:網頁圖標中間件,指定favicon的路徑

參數:options:

  • maxAge:緩存存活時間(毫秒),默認值1天

例子:新建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); 

網站圖標

23. limit

描述: 請求內容大小限制中間件,能夠用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 

24. query

描述: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的數據。

25. errorHadnler

沒法模擬出效果,暫時先跳過

描述:錯誤處理中間件,對於開發過程當中的錯誤,提供棧跟蹤和錯誤響應,接受3種類型text,html,json。

Text: text/plain是默認類型,返回一個簡單的棧跟蹤和錯誤消息

JSON:application/json,返回{‘error’:error}對象

HTML: 返回一個HTML錯誤頁面

參數:options:

  • showStack:返回錯誤信息和錯誤棧.默認值false
  • showMessage,只返回錯誤信息,默認值false
  • dumpExceptions, 輸出異常日誌,默認值false
  • logErrors,輸出錯誤日誌到文件,默認值false

例子:新建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)
相關文章
相關標籤/搜索