Node.js 教程
express()用來(lái)創(chuàng)建一個(gè)Express的程序。express()方法是express模塊導(dǎo)出的頂層方法。
var express = require('express'); var app = express();
express.static是Express中唯一的內(nèi)建中間件。它以server-static模塊為基礎(chǔ)開發(fā),負(fù)責(zé)托管 Express 應(yīng)用內(nèi)的靜態(tài)資源。 參數(shù)root為靜態(tài)資源的所在的根目錄。 參數(shù)options是可選的,支持以下的屬性:
屬性 | 描述 | 類型 | 默認(rèn)值 |
---|---|---|---|
dotfiles | 是否響應(yīng)點(diǎn)文件。供選擇的值有"allow","deny"和"ignore" | String | "ignore" |
etag | 使能或者關(guān)閉etag | Boolean | true |
extensions | 設(shè)置文件延期回退 | Boolean | true |
index | 發(fā)送目錄索引文件。設(shè)置false將不發(fā)送。 | Mixed | "index.html" |
lastModified | 設(shè)置文件在系統(tǒng)中的最后修改時(shí)間到Last-Modified頭部??赡艿娜≈涤?strong>false和true。 | Boolean | true |
maxAge | 在Cache-Control頭部中設(shè)置max-age屬性,精度為毫秒(ms)或則一段ms format的字符串 | Number | 0 |
redirect | 當(dāng)請(qǐng)求的pathname是一個(gè)目錄的時(shí)候,重定向到尾隨"/" | Boolean | true |
setHeaders | 當(dāng)響應(yīng)靜態(tài)文件請(qǐng)求時(shí)設(shè)置headers的方法 | Funtion | ? |
如果你想獲得更多關(guān)于使用中間件的細(xì)節(jié),你可以查閱Serving static files in Express。
app對(duì)象一般用來(lái)表示Express程序。通過(guò)調(diào)用Express模塊導(dǎo)出的頂層的express()方法來(lái)創(chuàng)建它:
var express = require('express'); var app = express(); app.get('/', function(req, res) { res.send('hello world!'); }); app.listen(3000);
app 對(duì)象具有以下的方法:
它還有一些屬性設(shè)置,這些屬性可以改變程序的行為。獲得更多的信息,可以查閱Application settings。
app.locals對(duì)象是一個(gè)javascript對(duì)象,它的屬性就是程序本地的變量。
app.locals.title // => 'My App' app.locals.email // => 'me@myapp.com'
一旦設(shè)定,app.locals的各屬性值將貫穿程序的整個(gè)生命周期,與其相反的是 res.locals ,它只在這次請(qǐng)求的生命周期中有效。
在程序中,你可以在渲染模板時(shí)使用這些本地變量。它們是非常有用的,可以為模板提供一些有用的方法,以及app級(jí)別的數(shù)據(jù)。通過(guò)req.app.locals(具體查看req.app),Locals可以在中間件中使用。
app.locals.title = 'My App'; app.locals.strftime = require('strftime'); app.locals.email = 'me@myapp.com';
app.mountpath屬性是子程序掛載的路徑模式。
一個(gè)子程序是一個(gè)express的實(shí)例,其可以被用來(lái)作為路由句柄來(lái)處理請(qǐng)求。
var express = require('express'); var app = express(); // the main app var admin = express(); // the sub app admin.get('/', function(req, res) { console.log(admin.mountpath); // /admin res.send('Admin Homepage'); }); app.use('/admin', admin); // mount the sub app
它和req對(duì)象的req.baseUrl )屬性比較相似,除了req.baseUrl是匹配的URL路徑,而不是匹配的模式。如果一個(gè)子程序被掛載在多條路徑模式,app.mountpath就是一個(gè)關(guān)于掛載路徑模式項(xiàng)的列表,如下面例子所示。
var admin = express(); admin.get('/', function (req, res) { console.log(admin.mountpath); // [ '/adm*n', '/manager' ] res.send('Admin Homepage'); }); var secret = express(); secret.get('/', function (req, res) { console.log(secret.mountpath); // /secr*t res.send('Admin Secret'); }); admin.use('/secr*t', secret); // load the 'secret' router on '/secr*t', on the 'admin' sub app app.use(['/adm*n', '/manager'], admin); // load the 'admin' router on '/adm*n' and '/manager', on the parent app
當(dāng)子程序被掛載到父程序時(shí),mount事件被發(fā)射。父程序?qū)ο笞鳛閰?shù),傳遞給回調(diào)方法。
var admin = express(); admin.on('mount', function(parent) { console.log('Admin Mounted'); console.log(parent); // refers to the parent app }); admin.get('/', function(req, res) { res.send('Admin Homepage'); }); app.use('/admin', admin);
app.all方法和標(biāo)準(zhǔn)的app.METHOD()方法相似,除了它匹配所有的HTTP動(dòng)詞。 對(duì)于給一個(gè)特殊前綴映射一個(gè)全局的邏輯處理,或者無(wú)條件匹配,它是很有效的。例如,如果你把下面內(nèi)容放在所有其他的路由定義的前面,它要求所有從這個(gè)點(diǎn)開始的路由需要認(rèn)證和自動(dòng)加載一個(gè)用戶。記住這些回調(diào)并不是一定是終點(diǎn):loadUser可以在完成了一個(gè)任務(wù)后,調(diào)用next()方法來(lái)繼續(xù)匹配隨后的路由。
app.all('*', requireAuthentication, loadUser);
或者這種相等的形式:
app.all('*', requireAuthentication); app.all('*', loadUser);
另一個(gè)例子是全局的白名單方法。這個(gè)例子和前面的很像,然而它只是限制以/api開頭的路徑。
app.all('/api/*', requireAuthentication);
路由HTTP DELETE請(qǐng)求到有特殊回調(diào)方法的特殊的路徑。獲取更多的信息,可以查閱routing guide。 你可以提供多個(gè)回調(diào)函數(shù),它們的行為和中間件一樣,除了這些回調(diào)可以通過(guò)調(diào)用next('router')來(lái)繞過(guò)剩余的路由回調(diào)。你可以使用這個(gè)機(jī)制來(lái)為一個(gè)路由設(shè)置一些前提條件,如果不能滿足當(dāng)前路由的處理?xiàng)l件,那么你可以傳遞控制到隨后的路由。
app.delete('/', function(req, res) { res.send('DELETE request to homepage'); });
設(shè)置類型為布爾的設(shè)置名為name的值為false,此處的name是app settings table中各屬性的一個(gè)。調(diào)用app.set('foo', false)和調(diào)用app.disable('foo')是等價(jià)的。 比如:
app.disable('trust proxy'); app.get('trust proxy'); // => false
返回true如果布爾類型的設(shè)置值name被禁用為false,此處的name是app settings table中各屬性的一個(gè)。
app.disabled('trust proxy'); // => true app.enable('trust proxy'); app.disabled('trust proxy'); // => false
設(shè)置布爾類型的設(shè)置值name為true,此處的name是
app.enable('trust proxy'); app.get('trust proxy'); // => true
返回true如果布爾類型的設(shè)置值name被啟動(dòng)為true,此處的name是app settings table中各屬性的一個(gè)。
app.enabled('trust proxy'); // => false app.enable('trust proxy'); app.enabled('trust proxy'); // => true
注冊(cè)給定引擎的回調(diào),用來(lái)渲染處理ext文件。 默認(rèn)情況下,Express需要使用require()來(lái)加載基于文件擴(kuò)展的引擎。例如,如果你嘗試渲染一個(gè)foo.jade文件,Express在內(nèi)部調(diào)用下面的內(nèi)容,同時(shí)緩存require()結(jié)果供隨后的調(diào)用,來(lái)加速性能。
app.engine('jade', require('jade').__express);
使用下面的方法對(duì)于那些沒有提供開箱即用的.__express方法的模板,或者你希望使用不同的模板引擎擴(kuò)展。 比如,使用EJS模板引擎來(lái)渲染.html文件:
app.engine('html', require('ejs').renderFile);
在這個(gè)例子中,EJS提供了一個(gè).renderFile方法,這個(gè)方法滿足了Express規(guī)定的簽名規(guī)則:(path, options, callback),然而記住在內(nèi)部它只是ejs.__express的一個(gè)別名,所以你可以在不做任何事的情況下直接使用.ejs擴(kuò)展。 一些模板引擎沒有遵循這種規(guī)范,consolidate.js庫(kù)映射模板引擎以下面的使用方式,所以他們可以無(wú)縫的和Express工作。
var engines = require('consolidate'); app.engine('haml', engines.haml); app.engine('html', engines.hogan);
獲得設(shè)置名為name的app設(shè)置的值,此處的name是app settings table中各屬性的一個(gè)。 如下:
app.get('title'); // => undefined app.set('title', 'My Site'); app.get('title'); // => 'My Site'
路由HTTP GET請(qǐng)求到有特殊回調(diào)的特殊路徑。獲取更多的信息,可以查閱routing guide。 你可以提供多個(gè)回調(diào)函數(shù),它們的行為和中間件一樣,除了這些回調(diào)可以通過(guò)調(diào)用next('router')來(lái)繞過(guò)剩余的路由回調(diào)。你可以使用這個(gè)機(jī)制來(lái)為一個(gè)路由設(shè)置一些前提條件,如果請(qǐng)求沒能滿足當(dāng)前路由的處理?xiàng)l件,那么傳遞控制到隨后的路由。
app.get('/', function(req, res) { res.send('GET request to homepage'); });
綁定程序監(jiān)聽端口到指定的主機(jī)和端口號(hào)。這個(gè)方法和Node中的http.Server.listen()是一樣的。
var express = require('express'); var app = express(); app.listen(3000);
通過(guò)調(diào)用express()返回得到的app實(shí)際上是一個(gè)JavaScript的Function,被設(shè)計(jì)用來(lái)作為一個(gè)回調(diào)傳遞給Node HTTP servers來(lái)處理請(qǐng)求。這樣,其就可以很簡(jiǎn)便的基于同一份代碼提供http和https版本,所以app沒有從這些繼承(它只是一個(gè)簡(jiǎn)單的回調(diào))。
var express = require('express'); var https = require('https'); var http = require('http'); http.createServer(app).listen(80); https.createServer(options, app).listen(443);
app.listen()方法是下面所示的一個(gè)便利的方法(只針對(duì)HTTP協(xié)議):
app.listen = function() { var server = http.createServer(this); return server.listen.apply(server, arguments); };
路由一個(gè)HTTP請(qǐng)求,METHOD是這個(gè)請(qǐng)求的HTTP方法,比如GET,PUT,POST等等,注意是小寫的。所以,實(shí)際的方法是app.get(),app.post(),app.put()等等。下面有關(guān)于方法的完整的表。
獲取更多信息,請(qǐng)看routing guide。 Express支持下面的路由方法,對(duì)應(yīng)與同名的HTTP方法:
|
|
|
如果使用上述方法時(shí),導(dǎo)致了無(wú)效的javascript的變量名,可以使用中括號(hào)符號(hào),比如,app['m-search']('/', function ...
你可以提供多個(gè)回調(diào)函數(shù),它們的行為和中間件一樣,除了這些回調(diào)可以通過(guò)調(diào)用next('router')來(lái)繞過(guò)剩余的路由回調(diào)。你可以使用這個(gè)機(jī)制來(lái)為一個(gè)路由設(shè)置一些前提條件,如果請(qǐng)求沒有滿足當(dāng)前路由的處理?xiàng)l件,那么傳遞控制到隨后的路由。
本API文檔把使用比較多的HTTP方法app.get(),app.post,app.put(),app.delete()作為一個(gè)個(gè)單獨(dú)的項(xiàng)進(jìn)行說(shuō)明。然而,其他上述列出的方法以完全相同的方式工作。
app.all()是一個(gè)特殊的路由方法,它不屬于HTTP協(xié)議中的規(guī)定的方法。它為一個(gè)路徑加載中間件,其對(duì)所有的請(qǐng)求方法都有效。
app.all('/secret', function (req, res) { console.log('Accessing the secret section...'); next(); // pass control to the next handler });
給路由參數(shù)添加回調(diào)觸發(fā)器,這里的name是參數(shù)名或者參數(shù)數(shù)組,function是回調(diào)方法?;卣{(diào)方法的參數(shù)按序是請(qǐng)求對(duì)象,響應(yīng)對(duì)象,下個(gè)中間件,參數(shù)值和參數(shù)名。 如果name是數(shù)組,會(huì)按照各個(gè)參數(shù)在數(shù)組中被聲明的順序?qū)⒒卣{(diào)觸發(fā)器注冊(cè)下來(lái)。還有,對(duì)于除了最后一個(gè)參數(shù)的其他參數(shù),在他們的回調(diào)中調(diào)用next()來(lái)調(diào)用下個(gè)聲明參數(shù)的回調(diào)。對(duì)于最后一個(gè)參數(shù),在回調(diào)中調(diào)用next()將調(diào)用位于當(dāng)前處理路由中的下一個(gè)中間件,如果name只是一個(gè)string那就和它是一樣的(就是說(shuō)只有一個(gè)參數(shù),那么就是最后一個(gè)參數(shù),和數(shù)組中最后一個(gè)參數(shù)是一樣的)。 例如,當(dāng):user出現(xiàn)在路由路徑中,你可以映射用戶加載的邏輯處理來(lái)自動(dòng)提供req.user給這個(gè)路由,或者對(duì)輸入的參數(shù)進(jìn)行驗(yàn)證。
app.param('user', function(req, res, next, id) { User.find(id, function(error, user) { if (err) { next(err); } else if (user){ req.user = user; } else { next(new Error('failed to load user')); } }); });
對(duì)于Param的回調(diào)定義的路由來(lái)說(shuō),他們是局部的。它們不會(huì)被掛載的app或者路由繼承。所以,定義在app上的Param回調(diào)只有是在app上的路由具有這個(gè)路由參數(shù)時(shí)才起作用。
在定義param的路由上,param回調(diào)都是第一個(gè)被調(diào)用的,它們?cè)谝粋€(gè)請(qǐng)求-響應(yīng)循環(huán)中都會(huì)被調(diào)用一次并且只有一次,即使多個(gè)路由都匹配,如下面的例子:
app.param('id', function(req, res, next, id) { console.log('CALLED ONLY ONCE'); next(); }); app.get('/user/:id', function(req, res, next) { console.log('although this matches'); next(); }); app.get('/user/:id', function(req, res) { console.log('and this mathces too'); res.end(); });
當(dāng)GET /user/42,得到下面的結(jié)果:
CALLED ONLY ONCE although this matches and this matches too
app.param(['id', 'page'], function(req, res, next, value) { console.log('CALLED ONLY ONCE with', value); next(); }); app.get('/user/:id/:page', function(req. res, next) { console.log('although this matches'); next(); }); app.get('/user/:id/:page', function (req, res, next) { console.log('and this matches too'); res.end(); });
當(dāng)執(zhí)行GET /user/42/3,結(jié)果如下:
CALLED ONLY ONCE with 42 CALLED ONLY ONCE with 3 although this matches and this mathes too
下面章節(jié)描述的app.param(callback)在v4.11.0之后被棄用。
通過(guò)只傳遞一個(gè)回調(diào)參數(shù)給app.param(name, callback)方法,app.param(naem, callback)方法的行為將被完全改變。這個(gè)回調(diào)參數(shù)是關(guān)于app.param(name, callback)該具有怎樣的行為的一個(gè)自定義方法,這個(gè)方法必須接受兩個(gè)參數(shù)并且返回一個(gè)中間件。 這個(gè)回調(diào)的第一個(gè)參數(shù)就是需要捕獲的url的參數(shù)名,第二個(gè)參數(shù)可以是任一的JavaScript對(duì)象,其可能在實(shí)現(xiàn)返回一個(gè)中間件時(shí)被使用。 這個(gè)回調(diào)方法返回的中間件決定了當(dāng)URL中包含這個(gè)參數(shù)時(shí)所采取的行為。 在下面的例子中,app.param(name, callback)參數(shù)簽名被修改成了app.param(name, accessId)。替換接受一個(gè)參數(shù)名和回調(diào),app.param()現(xiàn)在接受一個(gè)參數(shù)名和一個(gè)數(shù)字。
var express = require('express'); var app = express(); app.param(function(param, option){ return function(req, res, next, val) { if (val == option) { next(); } else { res.sendStatus(403); } } }); app.param('id', 1337); app.get('/user/:id', function(req, res) { res.send('Ok'); }); app.listen(3000, function() { console.log('Ready'); });
在這個(gè)例子中,app.param(name, callback)參數(shù)簽名保持和原來(lái)一樣,但是替換成了一個(gè)中間件,定義了一個(gè)自定義的數(shù)據(jù)類型檢測(cè)方法來(lái)檢測(cè)user id的類型正確性。
app.param(function(param, validator) { return function(req, res, next, val) { if (validator(val)) { next(); } else { res.sendStatus(403); } } }); app.param('id', function(candidate) { return !isNaN(parseFloat(candidate)) && isFinite(candidate); });
在使用正則表達(dá)式來(lái),不要使用.。例如,你不能使用/user-.+/來(lái)捕獲user-gami,用使用[\s\S]或者[\w\>W]來(lái)代替(正如/user-[\s\S]+/)。
//captures '1-a_6' but not '543-azser-sder' router.get('/[0-9]+-[[\w]]*', function); //captures '1-a_6' and '543-az(ser"-sder' but not '5-a s' router.get('/[0-9]+-[[\S]]*', function); //captures all (equivalent to '.*') router.get('[[\s\S]]*', function);
通過(guò)這個(gè)方法可以得到app典型的路徑,其是一個(gè)string。
var app = express() , blog = express() , blogAdmin = express(); app.use('/blog', blog); app.use('/admin', blogAdmin); console.log(app.path()); // '' console.log(blog.path()); // '/blog' console.log(blogAdmin.path()); // '/blog/admin'
如果app掛載很復(fù)雜下,那么這個(gè)方法的行為也會(huì)很復(fù)雜:一種更好用的方式是使用req.baseUrl來(lái)獲得這個(gè)app的典型路徑。
路由HTTP POST請(qǐng)求到有特殊回調(diào)的特殊路徑。獲取更多的信息,可以查閱[routing guide](http://expressjs.com/guide/routing.html)。
你可以提供多個(gè)回調(diào)函數(shù),它們的行為和中間件一樣,除了這些回調(diào)可以通過(guò)調(diào)用next('router')來(lái)繞過(guò)剩余的路由回調(diào)。你可以使用這個(gè)機(jī)制來(lái)為一個(gè)路由設(shè)置一些前提條件,如果請(qǐng)求沒能滿足當(dāng)前路由的處理?xiàng)l件,那么傳遞控制到隨后的路由。
app.post('/', function(req, res) { res.send('POST request to homepage') });
路由HTTP PUT請(qǐng)求到有特殊回調(diào)的特殊路徑。獲取更多的信息,可以查閱routing guide。
你可以提供多個(gè)回調(diào)函數(shù),它們的行為和中間件一樣,除了這些回調(diào)可以通過(guò)調(diào)用next('router')來(lái)繞過(guò)剩余的路由回調(diào)。你可以使用這個(gè)機(jī)制來(lái)為一個(gè)路由設(shè)置一些前提條件,如果請(qǐng)求沒能滿足當(dāng)前路由的處理?xiàng)l件,那么傳遞控制到隨后的路由。
app.put('/', function(req, res) { res.send('PUT request to homepage'); });
通過(guò)callback回調(diào)返回一個(gè)view渲染之后得到的HTML文本。它可以接受一個(gè)可選的參數(shù),可選參數(shù)包含了這個(gè)view需要用到的本地?cái)?shù)據(jù)。這個(gè)方法類似于res.render(),除了它不能把渲染得到的HTML文本發(fā)送給客戶端。
將app.render()當(dāng)作是可以生成渲染視圖字符串的工具方法。在res.render()內(nèi)部,就是使用的app.render()來(lái)渲染視圖。
如果使能了視圖緩存,那么本地變量緩存就會(huì)保留。如果你想在開發(fā)的過(guò)程中緩存視圖,設(shè)置它為true。在生產(chǎn)環(huán)境中,視圖緩存默認(rèn)是打開的。
app.render('email', function(err, html) { // ... }); app.render('email', {name:'Tobi'}, function(err, html) { // ... });
返回一個(gè)單例模式的路由的實(shí)例,之后你可以在其上施加各種HTTP動(dòng)作的中間件。使用app.route()來(lái)避免重復(fù)路由名字(例如錯(cuò)字錯(cuò)誤)--說(shuō)的意思應(yīng)該是使用app.router()這個(gè)單例方法來(lái)避免同一個(gè)路徑多個(gè)路由實(shí)例。
var app = express(); app.route('/events') .all(function(req, res, next) { // runs for all HTTP verbs first // think of it as route specific middleware! }) .get(function(req, res, next) { res.json(...); }) .post(function(req, res, next) { // maybe add a new event... })
給 name 設(shè)置項(xiàng)賦 value 值,name 是 Application settings 中屬性的一項(xiàng)。 對(duì)于一個(gè)類型是布爾型的屬性調(diào)用app.set('foo', ture)等價(jià)于調(diào)用app.enable('foo')。同樣的,調(diào)用app.set('foo', false)等價(jià)于調(diào)用app.disable('foo')。
可以使用app.get()來(lái)取得設(shè)置的值:
app.set('title', 'My Site'); app.get('title'); // 'My Site'
Application Settings
如果name是程序設(shè)置之一,它將影響到程序的行為。下邊列出了程序中的設(shè)置。
屬性 | 類型 | 值 | 默認(rèn) |
---|---|---|---|
case sensitive routing | Boolean | 啟用區(qū)分大小寫。 | 不啟用。對(duì)/Foo和/foo處理是一樣。 |
env | String | 環(huán)境模型。 | process.env.NODEENV(NODEENV環(huán)境變量)或者"development" |
etag | Varied | 設(shè)置ETag響應(yīng)頭??扇〉闹?,可以查閱etag options table。更多關(guān)于HTTP ETag header。 | weak |
jsonp callback name | String | 指定默認(rèn)JSONP回調(diào)的名稱。 | ?callback= |
json replacer | String | JSON替代品回調(diào) | null |
json spaces | Number | 當(dāng)設(shè)置了這個(gè)值后,發(fā)送縮進(jìn)空格美化過(guò)的JSON字符串。 | Disabled |
query parser | Varied | 設(shè)置值為false來(lái)禁用query parser,或者設(shè)置simple,extended,也可以自己實(shí)現(xiàn)query string解析函數(shù)。simple基于Node原生的query解析,querystring。 | "extend" |
strict routing | Boolean | 啟用嚴(yán)格的路由。 | 不啟用。對(duì)/foo和/foo/的路由處理是一樣。 |
subdomain offset | Number | 用來(lái)刪除訪問(wèn)子域的主機(jī)點(diǎn)分部分的個(gè)數(shù) | 2 |
trust proxy | Varied | 指示app在一個(gè)反向代理的后面,使用x-Forwarded-*來(lái)確定連接和客戶端的IP地址。注意:X-Forwarded-*頭部很容易被欺騙,所有檢測(cè)客戶端的IP地址是靠不住的。trust proxy默認(rèn)不啟用。當(dāng)啟用時(shí),Express嘗試通過(guò)前端代理或者一系列代理來(lái)獲取已連接的客戶端IP地址。req.ips屬性包含了已連接客戶端IP地址的一個(gè)數(shù)組。為了啟動(dòng)它,需要設(shè)置在下面trust proxy options table中定義的值。trust proxy的設(shè)置實(shí)現(xiàn)使用了proxy-addr包。如果想獲得更多的信息,可以查閱它的文檔 | Disable |
views | String or Array | view所在的目錄或者目錄數(shù)組。如果是一個(gè)數(shù)組,將按在數(shù)組中的順序來(lái)查找view。 | process.cwd() + '/views' |
view cache | Boolean | 啟用視圖模板編譯緩存。 | 在生成環(huán)境默認(rèn)開啟。 |
view engine | String | 省略時(shí),默認(rèn)的引擎被擴(kuò)展使用。 | |
x-powered-by | Boolean | 啟用X-Powered-By:ExpressHTTP頭部 | true |
trust proxy 選項(xiàng)設(shè)置 查閱Express behind proxies來(lái)獲取更多信息。
Type | Value |
---|---|
Boolean |
如果為true,客戶端的IP地址作為X-Forwarded-*頭部的最左邊的條目。如果為false,可以理解為app直接與英特網(wǎng)直連,客戶端的IP地址衍生自req.connection.remoteAddress。false是默認(rèn)設(shè)置。 |
IP addresses |
一個(gè)IP地址,子網(wǎng),或者一組IP地址,和委托子網(wǎng)。下面列出的是一個(gè)預(yù)先配置的子網(wǎng)名列表。
使用下面方法中的任何一種來(lái)設(shè)置IP地址: app.set('trust proxy', 'loopback') // specify a single subnet app.set('trust proxy', 'loopback, 123.123.123.123') // specify a subnet and an address app.set('trust proxy', 'loopback, linklocal, uniquelocal') // specify multiple subnets as CSV app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) // specify multiple subnets as an array 當(dāng)指定IP地址之后, 這個(gè)IP地址或子網(wǎng)會(huì)被設(shè)置了這個(gè)IP地址或子網(wǎng)的app排除在外, 最靠近程序服務(wù)的沒有委托的地址將被看做客戶端IP地址。 |
Number |
信任從反向代理到app中間小于等于n跳的連接為客戶端。 |
Function |
客戶自定義委托代理信任機(jī)制。如果你使用這個(gè),請(qǐng)確保你自己知道你在干什么。 app.set('trust proxy', function (ip) { if (ip === '127.0.0.1' || ip === '123.123.123.123') return true; // trusted IPs else return false; }) |
etag 設(shè)置選項(xiàng) ETag功能的實(shí)現(xiàn)使用了etag包。如果你需要獲得更多的信息,你可以查閱它的文檔。
Type | Value |
---|---|
Boolean |
設(shè)置為true,啟用weak ETag。這個(gè)是默認(rèn)設(shè)置。設(shè)置false,禁用所有的ETag。 |
String | 如果是strong,使能strong ETag。如果是weak,啟用weak ETag。 |
Function |
客戶自定義ETag方法的實(shí)現(xiàn). 如果你使用這個(gè),請(qǐng)確保你自己知道你在干什么。 app.set('etag', function (body, encoding) { return generateHash(body, encoding); // consider the function is defined }) |
掛載中間件方法到路徑上。如果路徑未指定,那么默認(rèn)為"/"。
一個(gè)路由將匹配任何路徑如果這個(gè)路徑以這個(gè)路由設(shè)置路徑后緊跟著"/"。比如:app.use('/appale', ...)將匹配"/apple","/apple/images","/apple/images/news"等。
中間件中的req.originalUrl是req.baseUrl和req.path的組合,如下面的例子所示。
app.use('/admin', function(req, res, next) { // GET 'http://www.example.com/admin/new' console.log(req.originalUrl); // '/admin/new' console.log(req.baseUrl); // '/admin' console.log(req.path);// '/new' });
在一個(gè)路徑上掛載一個(gè)中間件之后,每當(dāng)請(qǐng)求的路徑的前綴部分匹配了這個(gè)路由路徑,那么這個(gè)中間件就會(huì)被執(zhí)行。 由于默認(rèn)的路徑為/,中間件掛載沒有指定路徑,那么對(duì)于每個(gè)請(qǐng)求,這個(gè)中間件都會(huì)被執(zhí)行。
// this middleware will be executed for every request to the app. app.use(function(req, res, next) { console.log('Time: %d', Date.now()); next(); });
中間件方法是順序處理的,所以中間件包含的順序是很重要的。
// this middleware will not allow the request to go beyond it app.use(function(req, res, next) { res.send('Hello World'); }); // this middleware will never reach this route app.use('/', function(req, res) { res.send('Welcome'); });
路徑可以是代表路徑的一串字符,一個(gè)路徑模式,一個(gè)匹配路徑的正則表達(dá)式,或者他們的一組集合。
下面是路徑的簡(jiǎn)單的例子。
類型 | 實(shí)例 |
---|---|
Path |
// will match paths starting with /abcd app.use('/abcd', function (req, res, next) { next(); }) |
Path Pattern |
// will match paths starting with /abcd and /abd app.use('/abc?d', function (req, res, next) { next(); }) // will match paths starting with /abcd, /abbcd, /abbbbbcd and so on app.use('/ab+cd', function (req, res, next) { next(); }) // will match paths starting with /abcd, /abxcd, /abFOOcd, /abbArcd and so on app.use('/ab*cd', function (req, res, next) { next(); }) // will match paths starting with /ad and /abcd app.use('/a(bc)?d', function (req, res, next) { next(); }) |
Regular Expression |
// will match paths starting with /abc and /xyz app.use(//abc|/xyz/, function (req, res, next) { next(); }) |
Array |
// will match paths starting with /abcd, /xyza, /lmn, and /pqr app.use(['/abcd', '/xyza', //lmn|/pqr/], function (req, res, next) { next(); }) |
方法可以是一個(gè)中間件方法,一系列中間件方法,一組中間件方法或者他們的集合。由于router和app實(shí)現(xiàn)了中間件接口,你可以像使用其他任一中間件方法那樣使用它們。
用法 | 實(shí)例 |
---|---|
單個(gè)中間件 | 你可以局部定義和掛載一個(gè)中間件。
app.use(function (req, res, next) { next(); })一個(gè)router是有效的中間件。 var router = express.Router(); router.get('/', function (req, res, next) { next(); }) app.use(router);一個(gè)Express程序是一個(gè)有效的中間件。 var subApp = express(); subApp.get('/', function (req, res, next) { next(); }) app.use(subApp); |
一系列中間件 |
對(duì)于一個(gè)相同的掛載路徑,你可以掛載超過(guò)一個(gè)的中間件。
var r1 = express.Router(); r1.get('/', function (req, res, next) { next(); }) var r2 = express.Router(); r2.get('/', function (req, res, next) { next(); }) app.use(r1, r2); |
Array |
在邏輯上使用一個(gè)數(shù)組來(lái)組織一組中間件。如果你傳遞一組中間件作為第一個(gè)或者唯一的參數(shù),接著你需要指定掛載的路徑。
var r1 = express.Router(); r1.get('/', function (req, res, next) { next(); }) var r2 = express.Router(); r2.get('/', function (req, res, next) { next(); }) app.use('/', [r1, r2]); |
Combination |
你可以組合下面的所有方法來(lái)掛載中間件。
function mw1(req, res, next) { next(); } function mw2(req, res, next) { next(); } var r1 = express.Router(); r1.get('/', function (req, res, next) { next(); }); var r2 = express.Router(); r2.get('/', function (req, res, next) { next(); }); var subApp = express(); subApp.get('/', function (req, res, next) { next(); }); app.use(mw1, [mw2, r1, r2], subApp); |
下面是一些例子,在Express程序中使用express.static中間件。
為程序托管位于程序目錄下的public目錄下的靜態(tài)資源:
// GET /style.css etc app.use(express.static(__dirname + '/public'));
在/static路徑下掛載中間件來(lái)提供靜態(tài)資源托管服務(wù),只當(dāng)請(qǐng)求是以/static為前綴的時(shí)候。
// GET /static/style.css etc. app.use('/static', express.static(express.__dirname + '/public'));
通過(guò)在設(shè)置靜態(tài)資源中間件之后加載日志中間件來(lái)關(guān)閉靜態(tài)資源請(qǐng)求的日志。
app.use(express.static(__dirname + '/public')); app.use(logger());
托管靜態(tài)資源從不同的路徑,但./public路徑比其他更容易被匹配:
app.use(express.static(__dirname + '/public')); app.use(express.static(__dirname + '/files')); app.use(express.static(__dirname + '/uploads'));
req對(duì)象代表了一個(gè)HTTP請(qǐng)求,其具有一些屬性來(lái)保存請(qǐng)求中的一些數(shù)據(jù),比如query string,parameters,body,HTTP headers等等。在本文檔中,按照慣例,這個(gè)對(duì)象總是簡(jiǎn)稱為req(http響應(yīng)簡(jiǎn)稱為res),但是它們實(shí)際的名字由這個(gè)回調(diào)方法在那里使用時(shí)的參數(shù)決定。 如下例子:
app.get('/user/:id', function(req, res) { res.send('user' + req.params.id); });
其實(shí)你也可以這樣寫:
app.get('/user/:id', function(request, response) { response.send('user' + request.params.id); });
在Express 4中,req.files默認(rèn)在req對(duì)象中不再是可用的。為了通過(guò)req.files對(duì)象來(lái)獲得上傳的文件,你可以使用一個(gè)multipart-handling(多種處理的工具集)中間件,比如busboy,multer,formidable,multipraty,connect-multiparty或者pez。
這個(gè)屬性持有express程序?qū)嵗囊粋€(gè)引用,其可以作為中間件使用。
如果你按照這個(gè)模式,你創(chuàng)建一個(gè)模塊導(dǎo)出一個(gè)中間件,這個(gè)中間件只在你的主文件中require()它,那么這個(gè)中間件可以通過(guò)req.app來(lái)獲取express的實(shí)例。 例如:
// index.js app.get("/viewdirectory", require('./mymiddleware.js'));
// mymiddleware.js module.exports = function(req, res) { res.send('The views directory is ' + req.app.get('views')); };
一個(gè)路由實(shí)例掛載的Url路徑。
var greet = express.Router(); greet.get('/jp', function(req, res) { console.log(req.baseUrl); // greet res.send('Konichiwa!'); }); app.use('/greet', greet);
即使你使用的路徑模式或者一系列路徑模式來(lái)加載路由,baseUrl屬性返回匹配的字符串,而不是路由模式。下面的例子,greet路由被加載在兩個(gè)路徑模式上。
app.use(['/gre+t', 'hel{2}o'], greet); // load the on router on '/gre+t' and '/hel{2}o'
當(dāng)一個(gè)請(qǐng)求路徑是/greet/jp,baseUrl是/greet,當(dāng)一個(gè)請(qǐng)求路徑是/hello/jp,req.baseUrl是/hello。 req.baseUrl和app對(duì)象的mountpath屬性相似,除了app.mountpath返回的是路徑匹配模式。
在請(qǐng)求的body中保存的是提交的一對(duì)對(duì)鍵值數(shù)據(jù)。默認(rèn)情況下,它是undefined,當(dāng)你使用比如body-parser和multer這類解析body數(shù)據(jù)的中間件時(shí),它是填充的。 下面的例子,給你展示了怎么使用body-parser中間件來(lái)填充req.body。
var app = require('express'); var bodyParser = require('body-parser'); var multer = require('multer');// v1.0.5 var upload = multer(); // for parsing multipart/form-data app.use(bodyParser.json()); // for parsing application/json app.use(bodyParser.urlencoded({extended:true})); // for parsing application/x-www-form-urlencoded app.post('/profile', upload.array(), function(req, res, next) { console.log(req.body); res.json(req.body); });
當(dāng)使用cookie-parser中間件的時(shí)候,這個(gè)屬性是一個(gè)對(duì)象,其包含了請(qǐng)求發(fā)送過(guò)來(lái)的cookies。如果請(qǐng)求沒有帶cookies,那么其值為{}。
// Cookie: name=tj req.cookies.name // => "tj"
獲取更多信息,問(wèn)題,或者關(guān)注,可以查閱cookie-parser。
指示這個(gè)請(qǐng)求是否是新鮮的。其和req.stale是相反的。 當(dāng)cache-control請(qǐng)求頭沒有no-cache指示和下面中的任一一個(gè)條件為true,那么其就為true:
ps:If-None-Match作用: If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 當(dāng)用戶再次請(qǐng)求該資源時(shí),將在HTTP Request 中加入If-None-Match信息(ETag的值)。如果服務(wù)器驗(yàn)證資源的ETag沒有改變(該資源沒有更新),將返回一個(gè)304狀態(tài)告訴客戶端使用本地緩存文件。否則將返回200狀態(tài)和新的資源和Etag. 使用這樣的機(jī)制將提高網(wǎng)站的性能
req.fresh // => true
包含了源自HostHTTP頭部的hostname。
當(dāng)trust proxy設(shè)置項(xiàng)被設(shè)置為啟用值,X-Forwarded-Host頭部被使用來(lái)代替Host。這個(gè)頭部可以被客戶端或者代理設(shè)置。
// Host: "example.com" req.hostname // => "example.com"
當(dāng)trust proxy設(shè)置項(xiàng)被設(shè)置為啟用值,這個(gè)屬性包含了一組在X-Forwarded-For請(qǐng)求頭中指定的IP地址。不然,其就包含一個(gè)空的數(shù)組。這個(gè)頭部可以被客戶端或者代理設(shè)置。
例如,如果X-Forwarded-For是client,proxy1,proxy2,req.ips就是["clinet", "proxy1", "proxy2"],這里proxy2就是最遠(yuǎn)的下游。
req.url不是一個(gè)原生的Express屬性,它繼承自Node's http module。
這個(gè)屬性很像req.url;然而,其保留了原版的請(qǐng)求鏈接,允許你自由地重定向req.url到內(nèi)部路由。比如,app.use()的mounting特點(diǎn)可以重定向req.url跳轉(zhuǎn)到掛載點(diǎn)。
// GET /search?q=something req.originalUrl // => "/search?q=something"
一個(gè)對(duì)象,其包含了一系列的屬性,這些屬性和在路由中命名的參數(shù)名是一一對(duì)應(yīng)的。例如,如果你有/user/:name路由,name屬性可作為req.params.name。這個(gè)對(duì)象默認(rèn)值為{}。
// GET /user/tj req.params.name // => "tj"
當(dāng)你使用正則表達(dá)式來(lái)定義路由規(guī)則,捕獲組的組合一般使用req.params[n],這里的n是第幾個(gè)捕獲租。這個(gè)規(guī)則被施加在無(wú)名通配符匹配,比如/file/*的路由:
// GET /file/javascripts/jquery.js req.params[0] // => "javascripts/jquery.js"
包含請(qǐng)求URL的部分路徑。
// example.com/users?sort=desc req.path // => "/users"
當(dāng)在一個(gè)中間件中被調(diào)用,掛載點(diǎn)不包含在req.path中。你可以查閱app.use()獲得跟多的信息。
請(qǐng)求的協(xié)議,一般為http,當(dāng)啟用TLS加密,則為https。
當(dāng)trust proxy設(shè)置一個(gè)啟用的參數(shù),如果存在X-Forwarded-Proto頭部的話,其將被信賴和使用。這個(gè)頭部可以被客戶端或者代理設(shè)置。
req.ptotocol // => "http"
一個(gè)對(duì)象,為每一個(gè)路由中的query string參數(shù)都分配一個(gè)屬性。如果沒有query string,它就是一個(gè)空對(duì)象,{}。
// GET /search?q=tobi+ferret req.query.q // => "tobi ferret" // GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse req.query.order // => "desc" req.query.shoe.color // => "blue" req.query.shoe.type // => "converse"
當(dāng)前匹配的路由,其為一串字符。比如:
app.get('/user/:id?', function userIdHandler(req, res) { console.log(req.route); res.send('GET') })
前面片段的輸出為:
{ path:"/user/:id?" stack: [ { handle:[Function:userIdHandler], name:"userIdHandler", params:undefined, path:undefined, keys:[], regexp:/^/?$/i, method:'get' } ] methods:{get:true} }
一個(gè)布爾值,如果建立的是TLS的連接,那么就為true。等價(jià)于:
'https' == req.protocol;
當(dāng)使用cookie-parser中間件的時(shí)候,這個(gè)屬性包含的是請(qǐng)求發(fā)過(guò)來(lái)的簽名cookies,這個(gè)屬性取得的是不含簽名,可以直接使用的值。簽名的cookies保存在不同的對(duì)象中來(lái)體現(xiàn)開發(fā)者的意圖;不然,一個(gè)惡意攻擊可以被施加在req.cookie值上(它是很容易被欺騙的)。記住,簽名一個(gè)cookie不是把它藏起來(lái)或者加密;而是簡(jiǎn)單的防止篡改(因?yàn)楹灻褂玫募用苁撬饺说?。如果沒有發(fā)送簽名的cookie,那么這個(gè)屬性默認(rèn)為{}。
// Cookie: user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3 req.signedCookies.user // => "tobi"
為了獲取更多的信息,問(wèn)題或者關(guān)注,可以參閱cookie-parser。
指示這個(gè)請(qǐng)求是否是stale(陳舊的),它與req.fresh是相反的。更多信息,可以查看req.fresh。
req.stale // => true
請(qǐng)求中域名的子域名數(shù)組。
// Host: "tobi.ferrets.example.com" req.subdomains // => ["ferrets", "tobi"]
一個(gè)布爾值,如果X-Requested-With的值為XMLHttpRequest,那么其為true,其指示這個(gè)請(qǐng)求是被一個(gè)客服端庫(kù)發(fā)送,比如jQuery。
req.xhr // => true
檢查這個(gè)指定的內(nèi)容類型是否被接受,基于請(qǐng)求的Accept HTTP頭部。這個(gè)方法返回最佳匹配,如果沒有一個(gè)匹配,那么其返回undefined(在這個(gè)case下,服務(wù)器端應(yīng)該返回406和"Not Acceptable")。
type值可以是一個(gè)單的MIME type字符串(比如application/json),一個(gè)擴(kuò)展名比如json,一個(gè)逗號(hào)分隔的列表,或者一個(gè)數(shù)組。對(duì)于一個(gè)列表或者數(shù)組,這個(gè)方法返回最佳項(xiàng)(如果有的話)。
// Accept: text/html req.accepts('html'); // => "html" // Accept: text/*, application/json req.accepts('html'); // => "html" req.accepts('text/html'); // => "text/html" req.accepts(['json', 'text']); // => "json" req.accepts('application/json'); // => "application/json" // Accept: text/*, application/json req.accepts('image/png'); req.accepts('png'); // => undefined // Accept: text/*;q=.5, application/json req.accepts(['html', 'json']); // => "json"
獲取更多信息,或者如果你有問(wèn)題或關(guān)注,可以參閱accepts。
返回指定的字符集集合中第一個(gè)的配置的字符集,基于請(qǐng)求的Accept-CharsetHTTP頭。如果指定的字符集沒有匹配的,那么就返回false。 獲取更多信息,或者如果你有問(wèn)題或關(guān)注,可以參閱accepts。
返回指定的編碼集合中第一個(gè)的配置的編碼,基于請(qǐng)求的Accept-EncodingHTTP頭。如果指定的編碼集沒有匹配的,那么就返回false。 獲取更多信息,或者如果你有問(wèn)題或關(guān)注,可以參閱accepts。
返回指定的語(yǔ)言集合中第一個(gè)的配置的語(yǔ)言,基于請(qǐng)求的Accept-LanguageHTTP頭。如果指定的語(yǔ)言集沒有匹配的,那么就返回false。 獲取更多信息,或者如果你有問(wèn)題或關(guān)注,可以參閱accepts。
返回指定的請(qǐng)求HTTP頭部的域內(nèi)容(不區(qū)分大小寫)。Referrer和Referer的域內(nèi)容可互換。
req.get('Content-type'); // => "text/plain" req.get('content-type'); // => "text/plain" req.get('Something') // => undefined
其是req.header(field)的別名。
如果進(jìn)來(lái)的請(qǐng)求的Content-type頭部域匹配參數(shù)type給定的MIME type,那么其返回true。否則返回false。
// With Content-Type: text/html; charset=utf-8 req.is('html'); req.is('text/html'); req.is('text/*'); // => true // When Content-Type is application/json req.is('json'); req.is('application/json'); req.is('application/*'); // => true req.is('html'); // => false
獲取更多信息,或者如果你有問(wèn)題或關(guān)注,可以參閱type-is。
過(guò)時(shí)的??梢栽谶m合的情況下,使用req.params,req.body或者req.query。
返回當(dāng)前參數(shù)name的值。
// ?name=tobi req.param('name') // => "tobi" // POST name=tobi req.param('name') // => "tobi" // /user/tobi for /user/:name req.param('name') // => "tobi"
按下面給出的順序查找:
可選的,你可以指定一個(gè)defaultValue來(lái)設(shè)置一個(gè)默認(rèn)值,如果這個(gè)參數(shù)在任何一個(gè)請(qǐng)求的對(duì)象中都不能找到。
直接通過(guò)req.params,req.body,req.query取得應(yīng)該更加的清晰-除非你確定每一個(gè)對(duì)象的輸入。 Body-parser中間件必須加載,如果你使用req.param()。詳細(xì)請(qǐng)看req.body。
res對(duì)象代表了當(dāng)一個(gè)HTTP請(qǐng)求到來(lái)時(shí),Express程序返回的HTTP響應(yīng)。在本文檔中,按照慣例,這個(gè)對(duì)象總是簡(jiǎn)稱為res(http請(qǐng)求簡(jiǎn)稱為req),但是它們實(shí)際的名字由這個(gè)回調(diào)方法在那里使用時(shí)的參數(shù)決定。 例如:
app.get('/user/:id', function(req, res) { res.send('user' + req.params.id); });
這樣寫也是一樣的:
app.get('/user/:id', function(request, response) { response.send('user' + request.params.id); });
這個(gè)屬性持有express程序?qū)嵗囊粋€(gè)引用,其可以在中間件中使用。
res.app和請(qǐng)求對(duì)象中的req.app屬性是相同的。/p>
布爾類型的屬性,指示這個(gè)響應(yīng)是否已經(jīng)發(fā)送HTTP頭部。
app.get('/', function(req, res) { console.log(res.headersSent); // false res.send('OK'); // send之后就發(fā)送了頭部 console.log(res.headersSent); // true });
一個(gè)對(duì)象,其包含了本次請(qǐng)求的響應(yīng)中的變量和因此它的變量只提供給本次請(qǐng)求響應(yīng)的周期內(nèi)視圖渲染里使用(如果有視圖的話)。 其他方面,其和app.locals是一樣的。
這個(gè)參數(shù)在導(dǎo)出請(qǐng)求級(jí)別的信息是很有效的,這些信息比如請(qǐng)求路徑,已認(rèn)證的用戶,用戶設(shè)置等等。
app.use(function(req, res, next) { res.locals.user = req.user; res.locals.authenticated = !req.user.anonymous; next(); });
res.append()方法在Expresxs4.11.0以上版本才支持。
在指定的field的HTTP頭部追加特殊的值value。如果這個(gè)頭部沒有被設(shè)置,那么將用value新建這個(gè)頭部。value可以是一個(gè)字符串或者數(shù)組。
注意:在res.append()之后調(diào)用app.set()函數(shù)將重置前面設(shè)置的值。
res.append('Lind', ['<http://localhost>', '<http://localhost:3000>']); res.append('Set-Cookie', 'foo=bar;Path=/;HttpOnly'); res.append('Warning', '199 Miscellaneous warning');
設(shè)置HTTP響應(yīng)的Content-Disposition頭內(nèi)容為"attachment"。如果提供了filename,那么將通過(guò)res.type()獲得擴(kuò)展名來(lái)設(shè)置Content-Type,并且設(shè)置Content-Disposition內(nèi)容為"filename="parameter。
res.attachment(); // Content-Disposition: attachment res.attachment('path/to/logo.png'); // Content-Disposition: attachment; filename="logo.png" // Content-Type: image/png
設(shè)置name和value的cookie,value參數(shù)可以是一串字符或者是轉(zhuǎn)化為json字符串的對(duì)象。
options是一個(gè)對(duì)象,其可以有下列的屬性。
屬性 | 類型 | 描述 |
---|---|---|
domain | String | 設(shè)置cookie的域名。默認(rèn)是你本app的域名。 |
expires | Date | cookie的過(guò)期時(shí)間,GMT格式。如果沒有指定或者設(shè)置為0,則產(chǎn)生新的cookie。 |
httpOnly | Boolean | 這個(gè)cookie只能被web服務(wù)器獲取的標(biāo)示。 |
maxAge | String | 是設(shè)置過(guò)去時(shí)間的方便選項(xiàng),其為過(guò)期時(shí)間到當(dāng)前時(shí)間的毫秒值。 |
path | String | cookie的路徑。默認(rèn)值是/。 |
secure | Boolean | 標(biāo)示這個(gè)cookie只用被HTTPS協(xié)議使用。 |
signed | Boolean | 指示這個(gè)cookie應(yīng)該是簽名的。 |
res.cookie()所作的都是基于提供的options參數(shù)來(lái)設(shè)置Set-Cookie頭部。沒有指定任何的options,那么默認(rèn)值在RFC6265中指定。
使用實(shí)例:
res.cookie('name', 'tobi', {'domain':'.example.com', 'path':'/admin', 'secure':true}); res.cookie('remenberme', '1', {'expires':new Date(Date.now() + 90000), 'httpOnly':true});
maxAge 是一個(gè)方便設(shè)置過(guò)期時(shí)間的方便的選項(xiàng),其以當(dāng)前時(shí)間開始的毫秒數(shù)來(lái)計(jì)算。下面的示例和上面的第二條功效一樣。
res.cookie('rememberme', '1', {'maxAge':90000}, "httpOnly":true);
你可以設(shè)置傳遞一個(gè)對(duì)象作為value的參數(shù)。然后其將被序列化為Json字符串,被bodyParser()中間件解析。
res.cookie('cart', {'items':[1, 2, 3]}); res.cookie('cart', {'items':[1, 2, 3]}, {'maxAge':90000});
當(dāng)我們使用cookie-parser中間件的時(shí)候,這個(gè)方法也支持簽名的cookie。簡(jiǎn)單地,在設(shè)置options時(shí)包含signed選項(xiàng)為true。然后res.cookie()將使用傳遞給cookieParser(secret)的密鑰來(lái)簽名這個(gè)值。
res.cookie('name', 'tobi', {'signed':true});
根據(jù)指定的name清除對(duì)應(yīng)的cookie。更多關(guān)于options對(duì)象可以查閱res.cookie()。
res.cookie('name', 'tobi', {'path':'/admin'}); res.clearCookie('name', {'path':'admin'});
傳輸path指定文件作為一個(gè)附件。通常,瀏覽器提示用戶下載。默認(rèn)情況下,Content-Disposition頭部"filename="的參數(shù)為path(通常會(huì)出現(xiàn)在瀏覽器的對(duì)話框中)。通過(guò)指定filename參數(shù)來(lái)覆蓋默認(rèn)值。
當(dāng)一個(gè)錯(cuò)誤發(fā)生時(shí)或者傳輸完成,這個(gè)方法將調(diào)用fn指定的回調(diào)方法。這個(gè)方法使用res.sendFile()來(lái)傳輸文件。
res.download('/report-12345.pdf'); res.download('/report-12345.pdf', 'report.pdf'); res.download('report-12345.pdf', 'report.pdf', function(err) { // Handle error, but keep in mind the response may be partially-sent // so check res.headersSent if (err) { } else { // decrement a download credit, etc. } });
結(jié)束本響應(yīng)的過(guò)程。這個(gè)方法實(shí)際上來(lái)自Node核心模塊,具體的是response.end() method of http.ServerResponse。
用來(lái)快速結(jié)束請(qǐng)求,沒有任何的數(shù)據(jù)。如果你需要發(fā)送數(shù)據(jù),可以使用res.send()和res.json()這類的方法。
res.end(); res.status(404).end();
進(jìn)行內(nèi)容協(xié)商,根據(jù)請(qǐng)求的對(duì)象中AcceptHTTP頭部指定的接受內(nèi)容。它使用 req.accepts()來(lái)選擇一個(gè)句柄來(lái)為請(qǐng)求服務(wù),這些句柄按質(zhì)量值進(jìn)行排序。如果這個(gè)頭部沒有指定,那么第一個(gè)方法默認(rèn)被調(diào)用。當(dāng)不匹配時(shí),服務(wù)器將返回406"Not Acceptable",或者調(diào)用default回調(diào)。
Content-Type請(qǐng)求頭被設(shè)置,當(dāng)一個(gè)回調(diào)方法被選擇。然而你可以改變他,在這個(gè)方法中使用這些方法,比如res.set()或者res.type()。
下面的例子,將回復(fù){"message":"hey"},當(dāng)請(qǐng)求的對(duì)象中Accept頭部設(shè)置成"application/json"或者"*/json"(不過(guò)如果是*/*,然后這個(gè)回復(fù)就是"hey")。
res.format({ 'text/plain':function() { res.send('hey'); }, 'text/html':function() { res.send('<p>hey</p>'); }, 'application/json':function() { res.send({message:'hey'}); }, 'default':function() { res.status(406).send('Not Acceptable'); } })
除了規(guī)范化的MIME類型之外,你也可以使用拓展名來(lái)映射這些類型來(lái)避免冗長(zhǎng)的實(shí)現(xiàn):
res.format({ text:function() { res.send('hey'); }, html:function() { res.send('<p>hey</p>'); }, json:function() { res.send({message:'hey'}); } })
返回field指定的HTTP響應(yīng)的頭部。匹配是區(qū)分大小寫。
res.get('Content-Type'); // => "text/plain"
發(fā)送一個(gè)json的響應(yīng)。這個(gè)方法和將一個(gè)對(duì)象或者一個(gè)數(shù)組作為參數(shù)傳遞給res.send()方法的效果相同。不過(guò),你可以使用這個(gè)方法來(lái)轉(zhuǎn)換其他的值到j(luò)son,例如null,undefined。(雖然這些都是技術(shù)上無(wú)效的JSON)。
res.json(null); res.json({user:'tobi'}); res.status(500).json({error:'message'});
發(fā)送一個(gè)json的響應(yīng),并且支持JSONP。這個(gè)方法和res.json()效果相同,除了其在選項(xiàng)中支持JSONP回調(diào)。
res.jsonp(null) // => null res.jsonp({user:'tobi'}) // => {"user" : "tobi"} res.status(500).jsonp({error:'message'}) // => {"error" : "message"}
默認(rèn)情況下,jsonp的回調(diào)方法簡(jiǎn)單寫作callback??梢酝ㄟ^(guò)jsonp callback name設(shè)置來(lái)重寫它。
下面是一些例子使用JSONP響應(yīng),使用相同的代碼:
// ?callback=foo res.jsonp({user:'tobo'}) // => foo({"user":"tobi"}) app.set('jsonp callback name', 'cb') // ?cb=foo res.status(500).jsonp({error:'message'}) // => foo({"error":"message"})
連接這些links,links是以傳入?yún)?shù)的屬性形式提供,連接之后的內(nèi)容用來(lái)填充響應(yīng)的Link HTTP頭部。
res.links({ next:'http://api.example.com/users?page=2', last:'http://api.example.com/user?page=5' });
效果:
Link:<http://api.example.com/users?page=2>;rel="next", <http://api.example.com/users?page=5>;rel="last"
設(shè)置響應(yīng)的LocationHTTP頭部為指定的path參數(shù)。
res.location('/foo/bar'); res.location('http://example.com'); res.location('back');
當(dāng)path參數(shù)為back時(shí),其具有特殊的意義,其指定URL為請(qǐng)求對(duì)象的Referer頭部指定的URL。如果請(qǐng)求中沒有指定,那么其即為"/"。
Express傳遞指定的URL字符串作為回復(fù)給瀏覽器響應(yīng)中的Location頭部的值,不檢測(cè)和操作,除了back這個(gè)參數(shù)。瀏覽器會(huì)將用戶重定向到location設(shè)置的url或者Referer的url(back參數(shù)的情況)
重定向來(lái)源于指定path的URL,以及指定的HTTP status codestatus。如果你沒有指定status,status code默認(rèn)為"302 Found"。
res.redirect('/foo/bar'); res.redirect('http://example.com'); res.redirect(301, 'http://example.com'); res.redirect('../login');
重定向也可以是完整的URL,來(lái)重定向到不同的站點(diǎn)。
res.redirect('http://google.com');</p><p> 重定向也可以相對(duì)于主機(jī)的根路徑。比如,如果程序的路徑為<strong>http://example.com/admin/post/new</strong>,那么下面將重定向到<strong>http://example.com/admim</strong>:</p> <pre>res.redirect('/admin');
重定向也可以相對(duì)于當(dāng)前的URL。比如,來(lái)之于http://example.com/blog/admin/(注意結(jié)尾的/),下面將重定向到http://example.com/blog/admin/post/new。
res.redirect('post/new');
如果來(lái)至于http://example.com/blog/admin(沒有尾部/),重定向post/new,將重定向到http://example.com/blog/post/new。如果你覺得上面很混亂,可以把路徑段認(rèn)為目錄(有'/')或者文件,這樣是可以的。相對(duì)路徑的重定向也是可以的。如果你當(dāng)前的路徑為http://example.com/admin/post/new,下面的操作將重定向到http://example.com/admin/post:
res.redirect('..');
back將重定向請(qǐng)求到referer,當(dāng)沒有referer的時(shí)候,默認(rèn)為/。
res.redirect('back');
渲染一個(gè)視圖,然后將渲染得到的HTML文檔發(fā)送給客戶端??蛇x的參數(shù)為:
本地變量緩存使能視圖緩存。在開發(fā)環(huán)境中緩存視圖,需要手動(dòng)設(shè)置為true;視圖緩存在生產(chǎn)環(huán)境中默認(rèn)開啟。
// send the rendered view to the client res.render('index'); // if a callback is specified, the render HTML string has to be sent explicitly res.render('index', function(err, html) { res.send(html); }); // pass a local variable to the view res.render('user', {name:'Tobi'}, function(err, html) { // ... });
發(fā)送HTTP響應(yīng)。
body參數(shù)可以是一個(gè)Buffer對(duì)象,一個(gè)字符串,一個(gè)對(duì)象,或者一個(gè)數(shù)組。比如:
res.send(new Buffer('whoop')); res.send({some:'json'}); res.send('<p>some html</p>'); res.status(404).send('Sorry, we cannot find that!'); res.status(500).send({ error: 'something blew up' });
對(duì)于一般的非流請(qǐng)求,這個(gè)方法可以執(zhí)行許多有用的的任務(wù):比如,它自動(dòng)給Content-LengthHTTP響應(yīng)頭賦值(除非先前定義),也支持自動(dòng)的HEAD和HTTP緩存更新。
當(dāng)參數(shù)是一個(gè)Buffer對(duì)象,這個(gè)方法設(shè)置Content-Type響應(yīng)頭為application/octet-stream,除非事先提供,如下所示:
res.set('Content-Type', 'text/html'); res.send(new Buffer('<p>some html</p>'));
當(dāng)參數(shù)是一個(gè)字符串,這個(gè)方法設(shè)置Content-Type響應(yīng)頭為text/html:
res.send('<p>some html</p>');
當(dāng)參數(shù)是一個(gè)對(duì)象或者數(shù)組,Express使用JSON格式來(lái)表示:
res.send({user:'tobi'}); res.send([1, 2, 3]);
res.sendFile()從Express v4.8.0開始支持。
傳輸path指定的文件。根據(jù)文件的擴(kuò)展名設(shè)置Content-TypeHTTP頭部。除非在options中有關(guān)于root的設(shè)置,path一定是關(guān)于文件的絕對(duì)路徑。 下面的表提供了options參數(shù)的細(xì)節(jié):
屬性 | 描述 | 默認(rèn)值 | 可用版本 |
---|---|---|---|
maxAge | 設(shè)置Cache-Control的max-age屬性,格式為毫秒數(shù),或者是ms format的一串字符串 | 0 | |
root | 相對(duì)文件名的根目錄 | ||
lastModified | 設(shè)置Last-Modified頭部為此文件在系統(tǒng)中的最后一次修改時(shí)間。設(shè)置false來(lái)禁用它 | Enable | 4.9.0+ |
headers | 一個(gè)對(duì)象,包含了文件相關(guān)的HTTP頭部。 | ||
dotfiles | 是否支持點(diǎn)開頭文件名的選項(xiàng)。可選的值"allow","deny","ignore" | "ignore" | ? |
當(dāng)傳輸完成或者發(fā)生了什么錯(cuò)誤,這個(gè)方法調(diào)用fn回調(diào)方法。如果這個(gè)回調(diào)參數(shù)指定了和一個(gè)錯(cuò)誤發(fā)生,回調(diào)方法必須明確地通過(guò)結(jié)束請(qǐng)求-響應(yīng)循環(huán)或者傳遞控制到下個(gè)路由來(lái)處理響應(yīng)過(guò)程。
下面是使用了所有參數(shù)的使用res.sendFile()的例子:
app.get('/file/:name', function(req, res, next) { var options = { root:__dirname + '/public', dotfile:'deny', headers:{ 'x-timestamp':Date.now(), 'x-sent':true } }; var fileName = req.params.name; res.sendFile(fileName, options, function(err) { if (err) { console.log(err); res.status(err.status).end(); } else { console.log('sent', fileName); } }); });
res.sendFile提供了文件服務(wù)的細(xì)粒度支持,如下例子說(shuō)明:
app.get('/user/:uid/photos/:file', function(req, res) { var uid = req.params.uid , file = req.params.file; req.user.mayViewFilesFrom(uid, function(yes) { if (yes) { res.sendFile('/upload/' + uid + '/' + file); } else { res.status(403).send('Sorry! you cant see that.'); } }); })
獲取更多信息,或者你有問(wèn)題或者關(guān)注,可以查閱send。
設(shè)置響應(yīng)對(duì)象的HTTP status code為statusCode并且發(fā)送statusCode的相應(yīng)的字符串形式作為響應(yīng)的Body。
res.sendStatus(200); // equivalent to res.status(200).send('OK'); res.sendStatus(403); // equivalent to res.status(403).send('Forbidden'); res.sendStatus(404); // equivalent to res.status(404).send('Not Found'); res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
如果一個(gè)不支持的狀態(tài)被指定,這個(gè)HTTP status依然被設(shè)置為statusCode并且用這個(gè)code的字符串作為Body。
res.sendStatus(2000); // equivalent to res.status(2000).send('2000');
設(shè)置響應(yīng)對(duì)象的HTTP頭部field為value。為了一次設(shè)置多個(gè)值,那么可以傳遞一個(gè)對(duì)象為參數(shù)。
res.set('Content-Type', 'text/plain'); res.set({ 'Content-Type':'text/plain', 'Content-Length':'123', 'ETag':'123456' })
其和res.header(field [,value])效果一致。
使用這個(gè)方法來(lái)設(shè)置響應(yīng)對(duì)象的HTTP status。其是Node中response.statusCode的一個(gè)連貫性的別名。
res.status(403).end(); res.status(400).send('Bad Request'); res.status(404).sendFile('/absolute/path/to/404.png');
程序?qū)⒃O(shè)置Content-TypeHTTP頭部的MIME type,如果這個(gè)設(shè)置的type能夠被mime.lookup解析成正確的Content-Type。如果type中包含了/字符,那么程序會(huì)直接設(shè)置Content-Type為type。
res.type('.html'); // => 'text/html' res.type('html'); // => 'text/html' res.type('json'); // => 'application/json' res.type('application/json'); // => 'application/json' res.type('png'); // => image/png:
在沒有Vary應(yīng)答頭部時(shí)增加Vary應(yīng)答頭部。
ps:vary的意義在于告訴代理服務(wù)器/緩存/CDN,如何判斷請(qǐng)求是否一樣,vary中的組合就是服務(wù)器/緩存/CDN判斷的依據(jù),比如Vary中有User-Agent,那么即使相同的請(qǐng)求,如果用戶使用IE打開了一個(gè)頁(yè)面,再用Firefox打開這個(gè)頁(yè)面的時(shí)候,CDN/代理會(huì)認(rèn)為是不同的頁(yè)面,如果Vary中沒有User-Agent,那么CDN/代理會(huì)認(rèn)為是相同的頁(yè)面,直接給用戶返回緩存的頁(yè)面,而不會(huì)再去web服務(wù)器請(qǐng)求相應(yīng)的頁(yè)面。通俗的說(shuō)就相當(dāng)于field作為了一個(gè)緩存的key來(lái)判斷是否命中緩存
res.vary('User-Agent').render('docs');
一個(gè)router對(duì)象是一個(gè)單獨(dú)的實(shí)例關(guān)于中間件和路由。你可以認(rèn)為其是一個(gè)"mini-application"(迷你程序),其具有操作中間件和路由方法的能力。每個(gè)Express程序有一個(gè)內(nèi)建的app路由。 路由自身表現(xiàn)為一個(gè)中間件,所以你可以使用它作為app.use()方法的一個(gè)參數(shù)或者作為另一個(gè)路由的use()的參數(shù)。 頂層的express對(duì)象有一個(gè)Router()方法,你可以使用Router()來(lái)創(chuàng)建一個(gè)新的router對(duì)象。
res.vary('User-Agent').render('docs');
如下,可以創(chuàng)建一個(gè)路由:
var router = express.Router([options]);
options參數(shù)可以指定路由的行為,其有下列選擇:
屬性 | 描述 | 默認(rèn)值 | 可用性 |
---|---|---|---|
caseSensitive | 是否區(qū)分大小寫 | 默認(rèn)不啟用。對(duì)待/Foo和/foo一樣。 | |
mergeParams | 保存父路由的res.params。如果父路由參數(shù)和子路由參數(shù)沖突,子路由參數(shù)優(yōu)先。 | false | 4.5.0+ |
strict | 使能嚴(yán)格路由。 | 默認(rèn)不啟用,/foo和/foo/被路由一樣對(duì)待處理 | ? |
你可以將router當(dāng)作一個(gè)程序,可以在其上添加中間件和HTTP路由方法(例如get,put,post等等)。
// invoked for any requests passed to this router router.use(function(req, res, next) { // .. some logic here .. like any other middleware next(); }); // will handle any request that ends in /events // depends on where the router is "use()'d" router.get('/events', function(req, res, next) { // .. });
你可以在一個(gè)特別的根URL上掛載一個(gè)路由,這樣你就以將你的各個(gè)路由放到不同的文件中或者甚至是mini的程序。
// only requests to /calendar/* will be sent to our "router" app.use('/calendar', router);
這個(gè)方法和router.METHOD()方法一樣,除了這個(gè)方法會(huì)匹配所有的HTTP動(dòng)作。
這個(gè)方法對(duì)想映射全局的邏輯處理到特殊的路徑前綴或者任意匹配是十分有用的。比如,如果你放置下面所示的這個(gè)路由在其他路由的前面,那么其將要求從這個(gè)點(diǎn)開始的所有的路由進(jìn)行驗(yàn)證操作和自動(dòng)加載用戶信息。記住,這些全局的邏輯操作,不需要結(jié)束請(qǐng)求響應(yīng)周期:loaduser可以執(zhí)行一個(gè)任務(wù),然后調(diào)用next()來(lái)將執(zhí)行流程移交到隨后的路由。
router.all('*', requireAuthentication, loadUser);
相等的形式:
router.all('*', requireAuthentication) router.all('*', loadUser);
這是一個(gè)白名單全局功能的例子。這個(gè)例子很像前面的,不過(guò)其僅僅作用于以/api開頭的路徑:
router.all('/api/*', requireAuthentication);
router.METHOD()方法提供了路由方法在Express中,這里的METHOD是HTTP方法中的一個(gè),比如GET,PUT,POST等等,但router中的METHOD是小寫的。所以,實(shí)際的方法是router.get(),router.put(),router.post()等等。
你可以提供多個(gè)回調(diào)函數(shù),它們的行為和中間件一樣,除了這些回調(diào)可以通過(guò)調(diào)用next('router')來(lái)繞過(guò)剩余的路由回調(diào)。你可以使用這個(gè)機(jī)制來(lái)為一個(gè)路由設(shè)置一些前提條件,如果請(qǐng)求沒有滿足當(dāng)前路由的處理?xiàng)l件,那么傳遞控制到隨后的路由。
下面的片段可能說(shuō)明了最簡(jiǎn)單的路由定義。Experss轉(zhuǎn)換path字符串為正則表達(dá)式,用于內(nèi)部匹配傳入的請(qǐng)求。在匹配的時(shí)候,是不考慮Query strings,例如,"GET /"將匹配下面的路由,"GET /?name=tobi"也是一樣的。
router.get('/', function(req, res) { res.send('Hello World'); });
如果你對(duì)匹配的path有特殊的限制,你可以使用正則表達(dá)式,例如,下面的可以匹配"GET /commits/71dbb9c"和"GET /commits/71bb92..4c084f9"。
router.get(/^/commits/(w+)(?:..(w+))?$/, function(req, res) { var from = req.params[0]; var to = req.params[1]; res.send('commit range ' + from + '..' + to); });
給路由參數(shù)添加回調(diào)觸發(fā)器,這里的name是參數(shù)名,function是回調(diào)方法?;卣{(diào)方法的參數(shù)按序是請(qǐng)求對(duì)象,響應(yīng)對(duì)象,下個(gè)中間件,參數(shù)值和參數(shù)名。雖然name在技術(shù)上是可選的,但是自Express V4.11.0之后版本不推薦使用(見下面)。
不像app.param(),router.param()不接受一個(gè)數(shù)組作為路由參數(shù)。
例如,當(dāng):user出現(xiàn)在路由路徑中,你可以映射用戶加載的邏輯處理來(lái)自動(dòng)提供req.user給這個(gè)路由,或者對(duì)輸入的參數(shù)進(jìn)行驗(yàn)證。
router.param('user', function(req, res, next, id) { User.find(id, function(error, user) { if (err) { next(err); } else if (user){ req.user = user; } else { next(new Error('failed to load user')); } }); });
對(duì)于Param的回調(diào)定義的路由來(lái)說(shuō),他們是局部的。它們不會(huì)被掛載的app或者路由繼承。所以,定義在router上的param回調(diào)只有是在router上的路由具有這個(gè)路由參數(shù)時(shí)才起作用。
在定義param的路由上,param回調(diào)都是第一個(gè)被調(diào)用的,它們?cè)谝粋€(gè)請(qǐng)求-響應(yīng)循環(huán)中都會(huì)被調(diào)用一次并且只有一次,即使多個(gè)路由都匹配,如下面的例子:
router.param('id', function(req, res, next, id) { console.log('CALLED ONLY ONCE'); next(); }); router.get('/user/:id', function(req, res, next) { console.log('although this matches'); next(); }); router.get('/user/:id', function(req, res) { console.log('and this mathces too'); res.end(); });
當(dāng)GET /user/42,得到下面的結(jié)果:
CALLED ONLY ONCE although this matches and this matches too
下面章節(jié)描述的router.param(callback)在v4.11.0之后被棄用。
通過(guò)只傳遞一個(gè)回調(diào)參數(shù)給router.param(name, callback)方法,router.param(naem, callback)方法的行為將被完全改變。這個(gè)回調(diào)參數(shù)是關(guān)于router.param(name, callback)該具有怎樣的行為的一個(gè)自定義方法,這個(gè)方法必須接受兩個(gè)參數(shù)并且返回一個(gè)中間件。
這個(gè)回調(diào)的第一個(gè)參數(shù)就是需要捕獲的url的參數(shù)名,第二個(gè)參數(shù)可以是任一的JavaScript對(duì)象,其可能在實(shí)現(xiàn)返回一個(gè)中間件時(shí)被使用。 這個(gè)回調(diào)方法返回的中間件決定了當(dāng)URL中包含這個(gè)參數(shù)時(shí)所采取的行為。
在下面的例子中,router.param(name, callback)參數(shù)簽名被修改成了router.param(name, accessId)。替換接受一個(gè)參數(shù)名和回調(diào),router.param()現(xiàn)在接受一個(gè)參數(shù)名和一個(gè)數(shù)字。
var express = require('express'); var app = express(); var router = express.Router(); router.param(function(param, option){ return function(req, res, next, val) { if (val == option) { next(); } else { res.sendStatus(403); } } }); router.param('id', 1337); router.get('/user/:id', function(req, res) { res.send('Ok'); }); app.use(router); app.listen(3000, function() { console.log('Ready'); });
在這個(gè)例子中,router.param(name. callback)參數(shù)簽名保持和原來(lái)一樣,但是替換成了一個(gè)中間件,定義了一個(gè)自定義的數(shù)據(jù)類型檢測(cè)方法來(lái)檢測(cè)user id的類型正確性。
router.param(function(param, validator) { return function(req, res, next, val) { if (validator(val)) { next(); } else { res.sendStatus(403); } } }); router.param('id', function(candidate) { return !isNaN(parseFloat(candidate)) && isFinite(candidate); });
返回一個(gè)單例模式的路由的實(shí)例,之后你可以在其上施加各種HTTP動(dòng)作的中間件。使用router.route()來(lái)避免重復(fù)路由名字(例如錯(cuò)字錯(cuò)誤)--說(shuō)的意思應(yīng)該是使用router.route()這個(gè)單例方法來(lái)避免同一個(gè)路徑多個(gè)路由實(shí)例。
構(gòu)建在上面的router.param()例子之上,下面的代碼展示了怎么使用router.route()來(lái)指定各種HTTP方法的處理句柄。
var router = express.Router(); router.param('user_id', function(req, res, next, id) { // sample user, would actually fetch from DB, etc... req.user = { id:id, name:"TJ" }; next(); }); router.route('/users/:user_id') .all(function(req, res, next) { // runs for all HTTP verbs first // think of it as route specific middleware! next(); }) .get(function(req, res, next) { res.json(req.user); }) .put(function(req, res, next) { // just an example of maybe updating the user req.user.name = req.params.name; // save user ... etc res.json(req.user); }) .post(function(req, res, next) { next(new Error('not implemented')); }) .delete(function(req, res, next) { next(new Error('not implemented')); })
這種方法重復(fù)使用單個(gè)/usrs/:userid路徑來(lái)添加了各種的HTTP方法。
給可選的path參數(shù)指定的路徑掛載給定的中間件方法,未指定path參數(shù),默認(rèn)值為/。 這個(gè)方法類似于app.use()。一個(gè)簡(jiǎn)單的例子和用例在下面描述。查閱app.use()獲得更多的信息。
中間件就像一個(gè)水暖管道,請(qǐng)求在你定義的第一個(gè)中間件處開始,順著中間件堆棧一路往下,如果路徑匹配則處理這個(gè)請(qǐng)求。
var express = require('express'); var app = express(); var router = express.Router(); // simple logger for this router`s requests // all requests to this router will first hit this middleware router.use(function(req, res, next) { console.log('%s %s %s', req.method, req.url, req.path); next(); }) // this will only be invoked if the path starts with /bar form the mount ponit router.use('/bar', function(req, res, next) { // ... maybe some additional /bar logging ... next(); }) // always be invoked router.use(function(req, res, next) { res.send('hello world'); }) app.use('/foo', router); app.listen(3000);
對(duì)于中間件function,掛載的路徑是被剝離的和不可見的。關(guān)于這個(gè)特性主要的影響是對(duì)于不同的路徑,掛載相同的中間件可能對(duì)代碼不做改動(dòng),盡管其前綴已經(jīng)改變。
你使用router.use()定義中間件的順序很重要。中間們是按序被調(diào)用的,所以順序決定了中間件的優(yōu)先級(jí)。例如,通常日志是你將使用的第一個(gè)中間件,以便每一個(gè)請(qǐng)求都被記錄。
var logger = require('morgan'); router.use(logger()); router.use(express.static(__dirname + '/public')); router.use(function(req, res) { res.send('Hello'); });
現(xiàn)在為了支持你不希望記錄靜態(tài)文件請(qǐng)求,但為了繼續(xù)記錄那些定義在logger()之后的路由和中間件。你可以簡(jiǎn)單的將static()移動(dòng)到前面來(lái)解決:
router.use(express.static(__dirname + '/public')); router.use(logger()); router.use(function(req, res){ res.send('Hello'); });
另外一個(gè)確鑿的例子是從不同的路徑托管靜態(tài)文件,你可以將./public放到前面來(lái)獲得更高的優(yōu)先級(jí):
app.use(express.static(__dirname + '/public')); app.use(express.static(__dirname + '/files')); app.use(express.static(__dirname + '/uploads'));
router.use()方法也支持命名參數(shù),以便你的掛載點(diǎn)對(duì)于其他的路由而言,可以使用命名參數(shù)來(lái)進(jìn)行預(yù)加載,這樣做是很有益的。
原文地址:https://github.com/bajian/express_api_4.x_chinese