nodejs開(kāi)發(fā)——express路由與中間件
路由
通常HTTP URL的格式是這樣的:
http表示協(xié)議。
host表示主機(jī)。
port為端口,可選字段,不提供時(shí)默認(rèn)為80。
path指定請(qǐng)求資源的URI(Uniform Resource Identifier,統(tǒng)一資源定位符),如果URL中沒(méi)有給出path,一般會(huì)默認(rèn)成“/”(通常由瀏覽器或其它HTTP客戶(hù)端完成補(bǔ)充上)。
所謂路由,就是如何處理HTTP請(qǐng)求中的路徑部分。比如“http://xxx.com/users/profile”這個(gè)URL,路由將決定怎么處理/users/profile這個(gè)路徑。
來(lái)回顧我們?cè)贜ode.js開(kāi)發(fā)入門(mén)——Express安裝與使用中提供的express版本的HelloWorld代碼:
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World!'); }); app.listen(8000, function () { console.log('Hello World is listening at port 8000'); });
上面代碼里的app.get()調(diào)用,實(shí)際上就為我們的網(wǎng)站添加了一條路由,指定“/”這個(gè)路徑由get的第二個(gè)參數(shù)所代表的函數(shù)來(lái)處理。
express對(duì)象可以針對(duì)常見(jiàn)的HTTP方法指定路由,使用下面的方法:
app.METHOD(path, callback [, callback ...])
路由路徑
使用字符串的路由路徑示例:
// 匹配根路徑的請(qǐng)求 app.get('/', function (req, res) { res.send('root'); }); // 匹配 /about 路徑的請(qǐng)求 app.get('/about', function (req, res) { res.send('about'); }); // 匹配 /random.text 路徑的請(qǐng)求 app.get('/random.text', function (req, res) { res.send('random.text'); }); 使用字符串模式的路由路徑示例: // 匹配 acd 和 abcd app.get('/ab?cd', function(req, res) { res.send('ab?cd'); }); // 匹配 abcd、abbcd、abbbcd等 app.get('/ab+cd', function(req, res) { res.send('ab+cd'); }); // 匹配 abcd、abxcd、abRABDOMcd、ab123cd等 app.get('/ab*cd', function(req, res) { res.send('ab*cd'); }); // 匹配 /abe 和 /abcde app.get('/ab(cd)?e', function(req, res) { res.send('ab(cd)?e'); });
字符 ?、+、* 和 () 是正則表達(dá)式的子集,- 和 . 在基于字符串的路徑中按照字面值解釋。
使用正則表達(dá)式的路由路徑示例:
// 匹配任何路徑中含有 a 的路徑: app.get(/a/, function(req, res) { res.send('/a/'); }); // 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等 app.get(/.*fly$/, function(req, res) { res.send('/.*fly$/'); });
路由句柄
可以為請(qǐng)求處理提供多個(gè)回調(diào)函數(shù),其行為類(lèi)似 中間件。唯一的區(qū)別是這些回調(diào)函數(shù)有可能調(diào)用 next('route') 方法而略過(guò)其他路由回調(diào)函數(shù)??梢岳迷摍C(jī)制為路由定義前提條件,如果在現(xiàn)有路徑上繼續(xù)執(zhí)行沒(méi)有意義,則可將控制權(quán)交給剩下的路徑。
路由句柄有多種形式,可以是一個(gè)函數(shù)、一個(gè)函數(shù)數(shù)組,或者是兩者混合,如下所示.
使用一個(gè)回調(diào)函數(shù)處理路由:
app.get('/example/a', function (req, res) { res.send('Hello from A!'); });
使用多個(gè)回調(diào)函數(shù)處理路由(記得指定 next 對(duì)象):
app.get('/example/b', function (req, res, next) { console.log('response will be sent by the next function ...'); next(); }, function (req, res) { res.send('Hello from B!'); });
使用回調(diào)函數(shù)數(shù)組處理路由:
var cb0 = function (req, res, next) { console.log('CB0'); next(); } var cb1 = function (req, res, next) { console.log('CB1'); next(); } var cb2 = function (req, res) { res.send('Hello from C!'); } app.get('/example/c', [cb0, cb1, cb2]);
混合使用函數(shù)和函數(shù)數(shù)組處理路由:
var cb0 = function (req, res, next) { console.log('CB0'); next(); } var cb1 = function (req, res, next) { console.log('CB1'); next(); } app.get('/example/d', [cb0, cb1], function (req, res, next) { console.log('response will be sent by the next function ...'); next(); }, function (req, res) { res.send('Hello from D!');
METHOD可以是GET、POST等HTTP方法的小寫(xiě),例如app.get,app.post。path部分呢,既可以是字符串字面量,也可以是正則表達(dá)式。最簡(jiǎn)單的例子,把前面代碼里的app.get()調(diào)用的一個(gè)參數(shù)'/'修改為'*',含義就不一樣。改動(dòng)之前,只有訪(fǎng)問(wèn)“http://localhost:8000”或“http://localhost:8000/”這種形式的訪(fǎng)問(wèn)才會(huì)返回“Hello World!”,而改之后呢,像“http://localhost:8000/xxx/yyyy.zz”這種訪(fǎng)問(wèn)也會(huì)返回“Hello World!”。
使用express構(gòu)建Web服務(wù)器時(shí),很重要的一部分工作就是決定怎么響應(yīng)針對(duì)某個(gè)路徑的請(qǐng)求,也即路由處理。
最直接的路由配置方法,就是調(diào)用app.get()、app.post()一條一條的配置,不過(guò)對(duì)于需要處理大量路由的網(wǎng)站來(lái)講,這會(huì)搞出人命來(lái)的。所以呢,我們實(shí)際開(kāi)發(fā)中需要結(jié)合路由參數(shù)(query string、正則表達(dá)式、自定義的參數(shù)、post參數(shù))來(lái)減小工作量提高可維護(hù)性。更詳細(xì)的信息,參考http://expressjs.com/guide/routing.html。
中間件
Express里有個(gè)中間件(middleware)的概念。所謂中間件,就是在收到請(qǐng)求后和發(fā)送響應(yīng)之前這個(gè)階段執(zhí)行的一些函數(shù)。
要在一條路由的處理鏈上插入中間件,可以使用express對(duì)象的use方法。該方法原型如下:
app.use([path,] function [, function...])
當(dāng)app.use沒(méi)有提供path參數(shù)時(shí),路徑默認(rèn)為“/”。當(dāng)你為某個(gè)路徑安裝了中間件,則當(dāng)以該路徑為基礎(chǔ)的路徑被訪(fǎng)問(wèn)時(shí),都會(huì)應(yīng)用該中間件。比如你為“/abcd”設(shè)置了中間件,那么“/abcd/xxx”被訪(fǎng)問(wèn)時(shí)也會(huì)應(yīng)用該中間件。
中間件函數(shù)的原型如下:
function (req, res, next)
第一個(gè)參數(shù)是Request對(duì)象req。第二個(gè)參數(shù)是Response對(duì)象res。第三個(gè)則是用來(lái)驅(qū)動(dòng)中間件調(diào)用鏈的函數(shù)next,如果你想讓后面的中間件繼續(xù)處理請(qǐng)求,就需要調(diào)用next方法。
給某個(gè)路徑應(yīng)用中間件函數(shù)的典型調(diào)用是這樣的:
app.use('/abcd', function (req, res, next) { console.log(req.baseUrl); next(); })
app.static中間件
Express提供了一個(gè)static中間件,可以用來(lái)處理網(wǎng)站里的靜態(tài)文件的GET請(qǐng)求,可以通過(guò)express.static訪(fǎng)問(wèn)。
express.static的用法如下:
express.static(root, [options])
第一個(gè)參數(shù)root,是要處理的靜態(tài)資源的根目錄,可以是絕對(duì)路徑,也可以是相對(duì)路徑。第二個(gè)可選參數(shù)用來(lái)指定一些選項(xiàng),比如maxAge、lastModified等,更多選項(xiàng)的介紹看這里:http://expressjs.com/guide/using-middleware.html#middleware.built-in。
一個(gè)典型的express.static應(yīng)用如下:
var options = { dotfiles: 'ignore', etag: false, extensions: ['htm', 'html'], index: false, maxAge: '1d', redirect: false, setHeaders: function (res, path, stat) { res.set('x-timestamp', Date.now()); } } app.use(express.static('public', options));
上面這段代碼將當(dāng)前路徑下的public目錄作為靜態(tài)文件,并且為Cache-Control頭部的max-age選項(xiàng)為1天。還有其它一些屬性,請(qǐng)對(duì)照express.static的文檔來(lái)理解。
使用express創(chuàng)建的HelloExpress項(xiàng)目的app.js文件里有這樣一行代碼:
app.use(express.static(path.join(__dirname, 'public')));
這行代碼將HelloExpress目錄下的public目錄作為靜態(tài)文件交給static中間件來(lái)處理,對(duì)應(yīng)的HTTP URI為“/”。path是一個(gè)Node.js模塊,__dirname是Node.js的全局變量,指向當(dāng)前運(yùn)行的js腳本所在的目錄。path.join()則用來(lái)拼接目錄。
有了上面的代碼,你就可以在瀏覽器里訪(fǎng)問(wèn)“http://localhost:3000/stylesheets/style.css”。我們做一點(diǎn)改動(dòng),把上面的代碼修改成下面這樣:
app.use('/static', express.static(path.join(__dirname, 'public')));
上面的代碼呢,針對(duì)/static路徑使用static中間件處理public目錄。這時(shí)你再用瀏覽器訪(fǎng)問(wèn)“http://localhost:3000/stylesheets/”就會(huì)看到一個(gè)404頁(yè)面,將地址換成“http://localhost:3000/static/stylesheets/style.css”就可以了。
Router
Express還提供了一個(gè)叫做Router的對(duì)象,行為很像中間件,你可以把Router直接傳遞給app.use,像使用中間件那樣使用Router。另外你還可以使用router來(lái)處理針對(duì)GET、POST等的路由,也可以用它來(lái)添加中間件,總之你可以將Router看作一個(gè)微縮版的app。
下面的代碼創(chuàng)建一個(gè)Router實(shí)例:
var router = express.Router([options]);
然后你就可以像使用app一樣使用router:
// 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) { // .. });
定義了router后,也可以將其作為中間件傳遞給app.use:
app.use('/events', router);
上面這種用法,會(huì)針對(duì)URL中的“/events”路徑應(yīng)用router,你在router對(duì)象上配置的各種路由策略和中間件,都會(huì)被在合適的時(shí)候應(yīng)用。
路由模塊
express工具創(chuàng)建的應(yīng)用,有一個(gè)routes目錄,下面保存了應(yīng)用到網(wǎng)站的Router模塊,index.js和user.js。這兩個(gè)模塊基本一樣,我們研究一下index.js。
下面是index.js的內(nèi)容:
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router;
index.js創(chuàng)建了一個(gè)Router實(shí)例,然后調(diào)用router.get為“/”路徑應(yīng)用了路由函數(shù)。最后呢使用module.exports將Router對(duì)象導(dǎo)出。
下面是app.js里引用到index.js的代碼:
var routes = require('./routes/index'); ... app.use('/', routes);
第一處,require(‘./routes/index')將其作為模塊使用,這行代碼導(dǎo)入了index.js,并且將index.js導(dǎo)出的router對(duì)象保存在變量routes里以供后續(xù)使用。注意,上面代碼里的routes就是index.js里的router。
第二處代碼,把routes作為一個(gè)中間件,掛載到了“/”路徑上。
模塊
前面分析index.js時(shí)看到了module.exports的用法。module.exports用來(lái)導(dǎo)出一個(gè)Node.js模塊內(nèi)的對(duì)象,調(diào)用者使用require加載模塊時(shí),就會(huì)獲得導(dǎo)出的對(duì)象的實(shí)例。
我們的index.js導(dǎo)出了Router對(duì)象。app.js使用require(‘./routes/index')獲取了一個(gè)Router實(shí)例。
module.exports還有一個(gè)輔助用法,即直接使用exports來(lái)導(dǎo)出。
exports.signup = function(req, res){ //some code } exports.login = function(req, res){ //some code }
上面的代碼(假定在users.js文件里)直接使用exports來(lái)導(dǎo)出。當(dāng)使用exports來(lái)導(dǎo)出時(shí),你設(shè)置給exports的屬性和方法,實(shí)際上都是module.exports的。這個(gè)模塊最終導(dǎo)出的是module.exports對(duì)象,你使用類(lèi)似“exports.signup”這種形式設(shè)置的方法或?qū)傩?,調(diào)用方在require后都可以直接使用。
使用users模塊的代碼可能是這樣的:
var express = require('express'); var app = express(); ... var users = require('./routes/users'); app.post('/signup', users.signup); app.post('/login', users.login); ...
1. 什么是router路徑,什么是middleware?
我們輸入www.baidu.com 來(lái)訪(fǎng)問(wèn)百度的主頁(yè),瀏覽器會(huì)自動(dòng)轉(zhuǎn)換為 http://www.baidu.com:80/(省略一些參數(shù))。 http://代表我們同服務(wù)器連接使用的是http協(xié)議,www.baidu.com 代表的是服務(wù)器的主機(jī)地址,會(huì)被我們的pc通過(guò)DNS解析為IP地址。80是默認(rèn)的應(yīng)用層端口。/ 即為我們?cè)L問(wèn)的服務(wù)器(www.baidu.com)的路徑,服務(wù)器要對(duì)我們?cè)L問(wèn)的這個(gè)路徑做出響應(yīng),采取一定的動(dòng)作。我們可以把這一過(guò)程看做一個(gè)路由。
訪(fǎng)問(wèn)的路徑‘/'即為router的路徑,服務(wù)器采取的動(dòng)作即為middleware,即為一個(gè)個(gè)特殊的函數(shù)。
2. router路徑
www.baidu.com/test: 路徑為 /test
www.baidu.com/test?name=1&number=2: 路徑同樣為/test, ?后面會(huì)被服務(wù)器理解傳給路徑的參數(shù)。
3. Middleware
An Express application is essentially a stack of middleware which are executed serially.(express應(yīng)用其實(shí)就是由一系列順序執(zhí)行的Middleware組成。)
A middleware is a function with access to the request object (req), the response object (res), and the next middleware in line in the request-response cycle of an Express application. It is commonly denoted by a variable named next. Each middleware has the capacity to execute any code, make changes to the request and the reponse object, end the request-response cycle, and call the next middleware in the stack. Since middleware are execute serially, their order of inclusion is important.(中間件其實(shí)就是一個(gè)訪(fǎng)問(wèn)express應(yīng)用串入的req,res,nex參數(shù)的函數(shù),這個(gè)函數(shù)可以訪(fǎng)問(wèn)任何通過(guò)req,res傳入的資源。)
If the current middleware is not ending the request-response cycle, it is important to call next() to pass on the control to the next middleware, else the request will be left hanging.(如果當(dāng)前中間件沒(méi)有完成對(duì)網(wǎng)頁(yè)的res響應(yīng) ,還可以通過(guò)next把router 留給下一個(gè)middleware繼續(xù)執(zhí)行)
With an optional mount path, middleware can be loaded at the application level or at the router level. Also, a series of middleware functions can be loaded together, creating a sub-stack of middleware system at a mount point.
路由的產(chǎn)生是通過(guò)HTTP的各種方法(GET, POST)產(chǎn)生的,Middleware可以跟router路徑跟特定的HTTP方法綁定,也可以跟所有的方法綁定。
3.1 通過(guò)express應(yīng)用的use(all),把Middleware同router路徑上的所有HTTP方法綁定:
app.use(function (req, res, next) { console.log('Time: %d', Date.now()); next(); })
3.2 通過(guò)express應(yīng)用的http.verb,把Middleware同router路徑上的特定的HTTP方法綁定:
app.get('/', function(req, res){ res.send('hello world'); }); app.post('/', function(req, res){ res.send('hello world'); });
4. Express的Router對(duì)象
當(dāng)express實(shí)例的路由越來(lái)越多的時(shí)候,最好把路由分類(lèi)獨(dú)立出去,express的實(shí)例(app) 能更好的處理其他邏輯流程。Express的Router對(duì)象是一個(gè)簡(jiǎn)化的 app實(shí)例,只具有路由相關(guān)的功能,包括use, http verbs等等。最后這個(gè)Router再通過(guò)app的use掛載到app的相關(guān)路徑下。
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 ends in /bar router.use('/bar', function(req, res, next) { // ... maybe some additional /bar logging ... next(); }); // always invoked router.use(function(req, res, next) { res.send('Hello World'); }); app.use('/foo', router); app.listen(3000);
router的路由必須通過(guò)app.use和app.verbs 掛載到app上才能被響應(yīng)。所以上述代碼,只有在app捕捉到 /foo路徑上的路由時(shí),才能router中定義的路由,雖然router中有針對(duì) '/' 的路由,但是被app中的路由給覆蓋了。
附:app.verbs和app.use的路由路徑區(qū)別:
先看一段測(cè)試代碼:
var express = require('express'); var app = express(); var router = express.Router(); app.get('/', function(req, res){ console.log('test1'); }); app.use('/', function(req, res){ console.log('test2'); }); router.get('/', function(req, res){ console.log('test3'); }); app.listen(4000);
輸入url: localhost:4000
輸出結(jié)果:test1
輸入url: localhost:4000/hello
輸出結(jié)果:test2
結(jié)論:app.get掛載‘/'的路由只響應(yīng)跟'/'精確匹配的GET請(qǐng)求。 而app.use掛載的'/'的路由響應(yīng)所有以'/' 為起始路由的路由,且不限制HTTP訪(fǎng)問(wèn)的方法。以下說(shuō)明:Mounting a middleware at a path will cause the middleware function to be executed whenever the base of the requested path matches the path.
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Express的路由詳解
- 詳解NodeJS框架express的路徑映射(路由)功能及控制
- 詳解nuxt路由鑒權(quán)(express模板)
- 基于express中路由規(guī)則及獲取請(qǐng)求參數(shù)的方法
- Nodejs封裝類(lèi)似express框架的路由實(shí)例詳解
- nodeJS?express路由學(xué)習(xí)req.body與req.query方法實(shí)例詳解
- vue路由history模式頁(yè)面刷新404解決方法Koa?Express
- Node Express用法詳解【安裝、使用、路由、中間件、模板引擎等】
- nodejs?express路由匹配控制及Router模塊化使用詳解
- 淺探express路由和中間件的實(shí)現(xiàn)
- NodeJs?Express路由使用流程解析
- Express框架定制路由實(shí)例分析
相關(guān)文章
一文詳解Node中module.exports和exports區(qū)別
這篇文章主要介紹了一文詳解Node中module.exports和exports區(qū)別2023-03-03輕松創(chuàng)建nodejs服務(wù)器(2):nodejs服務(wù)器的構(gòu)成分析
這篇文章主要介紹了輕松創(chuàng)建nodejs服務(wù)器(2):nodejs服務(wù)器的構(gòu)成分析,本文是對(duì)第一節(jié)中簡(jiǎn)單服務(wù)器的代碼進(jìn)行分析總結(jié),需要的朋友可以參考下2014-12-12Node.js實(shí)現(xiàn)解析post請(qǐng)求的方法詳解
這篇文章主要為大家詳細(xì)介紹了Node.js實(shí)現(xiàn)解析post請(qǐng)求方法的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,有需要的小伙伴可以了解下2024-04-04nodejs Assert中equal(),strictEqual(),deepEqual(),strictDeepEq
這篇文章主要介紹了node項(xiàng)目中遇到使用斷言測(cè)試程序,看了下nodejs的api之后,對(duì)nodejs中assert斷言的幾個(gè)方法做個(gè)簡(jiǎn)單的記錄,需要的朋友可以參考下2017-09-09node.js文件上傳重命名以及移動(dòng)位置的示例代碼
本篇文章主要介紹了node.js文件上傳重命名以及移動(dòng)位置的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01node.js中的http.response.addTrailers方法使用說(shuō)明
這篇文章主要介紹了node.js中的http.response.addTrailers方法使用說(shuō)明,本文介紹了http.response.addTrailers的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12Nodejs 微信小程序消息推送的實(shí)現(xiàn)
這篇文章主要介紹了Nodejs 微信小程序消息推送的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01