您好,登錄后才能下訂單哦!
一、路由
1、通常HTTP URL的格式是這樣的:http://host[:port][path]
所謂路由,就是如何處理HTTP請求中的路徑部分。比如“http://xxx.com:80/users/profile”
這個URL,路由將決定怎么處理/users/profile這個路徑。
路由是由一個 URI、HTTP 請求(GET、POST等)和若干個句柄組成,它的結構如下: app.METHOD(path, [callback...], callback), app 是 express對象的一個實例, METHOD 是一個 HTTP 請求方法, path 是服務器上的路徑, callback 是當路由匹配時要執行的函數。
//使用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()調用,實際上就為我們的網站添加了一條路由,指定“/”這個路徑,由get的第二個參數所代表的函數來處理。
express對象可以針對常見的HTTP方法指定路由,使用下面的方法:app.METHOD(path, callback [, callback ...])
2、路由方法
路由方法源于 HTTP 請求方法,和 express 實例相關聯。
下面這個例子展示了為應用跟路徑定義的 GET 和 POST 請求:
// GET method route
app.get('/', function (req, res) {
res.send('GET request to the homepage');
});
// POST method route
app.post('/', function (req, res) {
res.send('POST request to the homepage');
});
app.all() 是一個特殊的路由方法,沒有任何 HTTP 方法與其對應,它的作用是對于一個路徑上的所有請求加載中間件。
在下面的例子中,來自 “/secret” 的請求,不管使用 GET、POST、PUT、DELETE 或其他任何 http 模塊支持的 HTTP 請求,句柄都會得到執行。
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...');
next(); // pass control to the next handler
});
3、路由路徑
路由路徑和請求方法一起定義了請求的端點,它可以是字符串、字符串模式或者正則表達式。
使用字符串的路由路徑示例:
// 匹配根路徑的請求
app.get('/', function (req, res) {
res.send('root');
});
// 匹配 /about 路徑的請求
app.get('/about', function (req, res) {
res.send('about');
});
// 匹配 /random.text 路徑的請求
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');
});
4、路由句柄
可以為請求處理,提供多個回調函數,其行為類似 中間件。唯一的區別是這些回調函數有可能調用 next('route') 方法而略過其他路由回調函數。可以利用該機制為路由定義前提條件,如果在現有路徑上繼續執行沒有意義,則可將控制權交給剩下的路徑。
路由句柄有多種形式,可以是一個函數、一個函數數組,或者是兩者混合,如下所示.
//使用一個回調函數處理路由:
app.get('/example/a', function (req, res) {
res.send('Hello from A!');
});
//使用多個回調函數處理路由(記得指定 next 對象):
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!');
});
//使用回調函數數組處理路由:
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]);
//混合使用函數和函數數組處理路由:
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方法的小寫,例如app.get,app.post。path部分呢,既可以是字符串字面量,也可以是正則表達式。最簡單的例子,把前面代碼里的app.get()調用的一個參數’/’修改為’*’,含義就不一樣。改動之前,只有訪問“http://localhost:8000”或“http://localhost:8000/”這種形式的訪問才會返回“Hello World!”,而改之后呢,像“http://localhost:8000/xxx/yyyy.zz”這種訪問也會返回“Hello World!”。
使用express構建Web服務器時,很重要的一部分工作就是決定怎么響應針對某個路徑的請求,也即路由處理。
最直接的路由配置方法,就是調用app.get()、app.post()一條一條的配置,不過對于需要處理大量路由的網站來講,這會搞出人命來的。所以呢,我們實際開發中需要結合路由參數(query string、正則表達式、自定義的參數、post參數)來減小工作量提高可維護性。
5、響應方法
下表中響應對象(res)的方法向客戶端返回響應,終結請求響應的循環。如果在路由句柄中一個方法也不調用,來自客戶端的請求會一直掛起。
6、app.route()
可使用 app.route() 創建路由路徑的鏈式路由句柄。由于路徑在一個地方指定,這樣做有助于創建模塊化的路由,而且減少了代碼冗余和拼寫錯誤。請參考 Router() 文檔 了解更多有關路由的信息。
下面這個示例程序使用 app.route() 定義了鏈式路由句柄。
app.route('/book')
.get(function(req, res) {
res.send('Get a random book');
})
.post(function(req, res) {
res.send('Add a book');
})
.put(function(req, res) {
res.send('Update the book');
});
7、express.Router
express還提供了一個叫做Router的對象,行為很像中間件,你可以把Router直接傳遞給app.use,像使用中間件那樣使用Router。另外你還可以使用router來處理針對GET、POST等的路由,也可以用它來添加中間件,總之你可以將Router看作一個微縮版的app。
可使用 express.Router 類創建模塊化、可掛載的路由句柄。Router 實例是一個完整的中間件和路由系統,因此常稱其為一個 “mini-app”。
下面的實例程序創建了一個路由模塊,并加載了一個中間件,定義了一些路由,并且將它們掛載至應用的路徑上。
在 app 目錄下創建名為 birds.js 的文件,內容如下:
var express = require('express');
var router = express.Router();
// 該路由使用的中間件
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// 定義網站主頁的路由
router.get('/', function(req, res) {
res.send('Birds home page');
});
// 定義 about 頁面的路由
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;
然后在應用中加載路由模塊:
var birds = require('./birds');
app.use('/birds', birds);
應用即可處理發自 /birds 和 /birds/about 的請求,并且調用為該路由指定的 timeLog 中間件。
上面這種用法,會針對URL中的“/birds”路徑應用router,你在router對象上配置的各種路由策略和中間件,都會被在合適的時候應用。
8、路由模塊
express工具創建的應用,有一個routes目錄,下面保存了應用到網站的Router模塊,index.js和user.js。這兩個模塊基本一樣,我們研究一下index.js。
//index.js
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創建了一個Router實例,然后調用router.get為“/”路徑應用了路由函數。最后呢使用module.exports將Router對象導出。
下面是app.js里引用到index.js的代碼:
//app.js
var routes = require('./routes/index');
...
app.use('/', routes);
9、模塊
前面分析index.js時看到了module.exports的用法。module.exports用來導出一個Node.js模塊內的對象,調用者使用require加載模塊時,就會獲得導出的對象的實例。
我們的index.js導出了Router對象。app.js使用require(‘./routes/index’)獲取了一個Router實例。
module.exports還有一個輔助用法,即直接使用exports來導出。
exports.signup = function(req, res){
//some code
}
exports.login = function(req, res){
//some code
}
上面的代碼(假定在users.js文件里)直接使用exports來導出。當使用exports來導出時,你設置給exports的屬性和方法,實際上都是module.exports的。這個模塊最終導出的是module.exports對象,你使用類似“exports.signup”這種形式設置的方法或屬性,調用方在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、概念
express里有個中間件(middleware)的概念。所謂中間件,就是在收到請求后和發送響應之前這個階段執行的一些函數。
中間件(Middleware) 是一個函數,它可以訪問請求對象(request object (req)), 響應對象(response object (res)), 和 web 應用中處于請求-響應循環流程中的中間件,一般被命名為 next 的變量。
中間件的功能包括:
app.use([path,] function [, function...])
function (req, res, next)
app.use('/abcd', function (req, res, next) {
console.log(req.baseUrl);
next();
})
Express 應用可使用如下幾種中間件:
express.static(root, [options])
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));
上面這段代碼將當前路徑下的public目錄作為靜態文件,并且為Cache-Control頭部的max-age選項為1天。還有其它一些屬性,請對照express.static的文檔來理解。
使用express創建的HelloExpress項目的app.js文件里有這樣一行代碼:
app.use(express.static(path.join(dirname, 'public')));
這行代碼將HelloExpress目錄下的public目錄作為靜態文件交給static中間件來處理,對應的HTTP URI為“/”。path是一個Node.js模塊,dirname是Node.js的全局變量,指向當前運行的js腳本所在的目錄。path.join()則用來拼接目錄。
有了上面的代碼,你就可以在瀏覽器里訪問“http://localhost:3000/stylesheets/style.css”。
我們做一點改動,把上面的代碼修改成下面這樣:
app.use('/static', express.static(path.join(__dirname, 'public')));
上面的代碼呢,針對/static路徑使用static中間件處理public目錄。這時你再用瀏覽器訪問“http://localhost:3000/stylesheets/”
就會看到一個404頁面,將地址成
“http://localhost:3000/static/stylesheets/style.css”
就可以了。
3、應用級中間件
應用級中間件綁定到 app 對象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是需要處理的 HTTP 請求的方法,例如 GET, PUT, POST 等等,全部小寫。例如:
var app = express();
// 沒有掛載路徑的中間件,應用的每個請求都會執行該中間件
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
// 掛載至 /user/:id 的中間件,任何指向 /user/:id 的請求都會執行它
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
// 路由和句柄函數(中間件系統),處理指向 /user/:id 的 GET 請求
app.get('/user/:id', function (req, res, next) {
res.send('USER');
});
4、下面這個例子展示了在一個掛載點裝載一組中間件。
// 一個中間件棧,對任何指向 /user/:id 的 HTTP 請求打印出相關信息
app.use('/user/:id', function(req, res, next) {
console.log('Request URL:', req.originalUrl);
next();
}, function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
5、作為中間件系統的路由句柄,使得為路徑定義多個路由成為可能。在下面的例子中,為指向 /user/:id 的 GET 請求定義了兩個路由。第二個路由雖然不會帶來任何問題,但卻永遠不會被調用,因為第一個路由已經終止了請求-響應循環。
// 一個中間件棧,處理指向 /user/:id 的 GET 請求
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id);
next();
}, function (req, res, next) {
res.send('User Info');
});
// 處理 /user/:id, 打印出用戶 id
app.get('/user/:id', function (req, res, next) {
res.end(req.params.id);
});
6、如果需要在中間件棧中跳過剩余中間件,調用 next('route') 方法將控制權交給下一個路由。 注意: next('route') 只對使用 app.VERB() 或 router.VERB() 加載的中間件有效。
// 一個中間件棧,處理指向 /user/:id 的 GET 請求
app.get('/user/:id', function (req, res, next) {
// 如果 user id 為 0, 跳到下一個路由
if (req.params.id == 0) next('route');
// 否則將控制權交給棧中下一個中間件
else next(); //
}, function (req, res, next) {
// 渲染常規頁面
res.render('regular');
});
// 處理 /user/:id, 渲染一個特殊頁面
app.get('/user/:id', function (req, res, next) {
res.render('special');
});
7、路由級中間件
路由級中間件和應用級中間件一樣,只是它綁定的對象為 express.Router()。
var router = express.Router();
路由級使用 router.use() 或 router.VERB() 加載。
上述在應用級創建的中間件系統,可通過如下代碼改寫為路由級:
var app = express();
var router = express.Router();
// 沒有掛載路徑的中間件,通過該路由的每個請求都會執行該中間件
router.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
// 一個中間件棧,顯示任何指向 /user/:id 的 HTTP 請求的信息
router.use('/user/:id', function(req, res, next) {
console.log('Request URL:', req.originalUrl);
next();
}, function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
// 一個中間件棧,處理指向 /user/:id 的 GET 請求
router.get('/user/:id', function (req, res, next) {
// 如果 user id 為 0, 跳到下一個路由
if (req.params.id == 0) next('route');
// 負責將控制權交給棧中下一個中間件
else next(); //
}, function (req, res, next) {
// 渲染常規頁面
res.render('regular');
});
// 處理 /user/:id, 渲染一個特殊頁面
router.get('/user/:id', function (req, res, next) {
console.log(req.params.id);
res.render('special');
});
// 將路由掛載至應用
app.use('/', router);
8、錯誤處理中間件
錯誤處理中間件有 4 個參數,定義錯誤處理中間件時必須使用這 4 個參數。即使不需要 next 對象,也必須在簽名中聲明它,否則中間件會被識別為一個常規中間件,不能處理錯誤。
錯誤處理中間件和其他中間件定義類似,只是要使用 4 個參數,而不是 3 個,其簽名如下: (err, req, res, next)。
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
9、內置中間件
從 4.x 版本開始,, Express 已經不再依賴 Connect 了。除了 express.static, Express 以前內置的中間件現在已經全部單獨作為模塊安裝使用了。
express.static(root, [options])
express.static 是 Express 唯一內置的中間件。它基于 serve-static,負責在 Express 應用中提托管靜態資源。
參數 root 指提供靜態資源的根目錄。
可選的 options 參數擁有如下屬性。
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));
每個應用可有多個靜態目錄。
app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));
9、第三方中間件
通過使用第三方中間件從而為 Express 應用增加更多功能。
安裝所需功能的 node 模塊,并在應用中加載,可以在應用級加載,也可以在路由級加載。
下面的例子安裝并加載了一個解析 cookie 的中間件: cookie-parser
$ npm install cookie-parser
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
// 加載用于解析 cookie 的中間件
app.use(cookieParser());
三、總結
1、什么是router路徑,什么是middleware?
我們輸入www.baidu.com 來訪問百度的主頁,瀏覽器會自動轉換為 http://www.baidu.com:80/(省略一些參數)。
http:// 代表我們同服務器連接使用的是http協議,
www.baidu.com 代表的是服務器的主機地址,會被我們的pc通過DNS解析為IP地址。
80是默認的應用層端口。
/ 即為我們訪問的服務器(www.baidu.com)的路徑,服務器要對我們訪問的這個路徑做出響應,采取一定的動作。我們可以把這一過程看做一個路由。
訪問的路徑‘/’即為router的路徑,服務器采取的動作即為middleware,即為一個個特殊的函數。
2. router路徑
www.baidu.com/test: 路徑為 /test
www.baidu.com/test?name=1&number=2: 路徑同樣為/test, ?后面會被服務器理解傳給路徑的參數。
3. Middleware
express應用其實就是由一系列順序執行的Middleware組成。
中間件其實就是一個訪問express應用傳入的req,res,nex參數的函數,這個函數可以訪問任何通過req,res傳入的資源。
如果當前中間件沒有完成對網頁的res響應 ,還可以通過next把router 留給下一middleware繼續執行
路由的產生是通過HTTP的各種方法(GET, POST)產生的,Middleware可以跟router路徑跟特定的HTTP方法綁定,也可以跟所有的方法綁定。
3.1 通過express應用的use(all),把Middleware同router路徑上的所有HTTP方法綁定:
app.use(function (req, res, next) {
console.log('Time: %d', Date.now());
next();
})
3.2 通過express應用的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對象
當express實例的路由越來越多的時候,最好把路由分類獨立出去,express的實例(app) 能更好的處理其他邏輯流程。Express的Router對象是一個簡化的 app實例,只具有路由相關的功能,包括use, http verbs等等。最后這個Router再通過app的use掛載到app的相關路徑下。
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的路由必須通過app.use和app.verbs 掛載到app上才能被響應。所以上述代碼,只有在app捕捉到 /foo路徑上的路由時,才能router中定義的路由,雖然router中有針對 '/' 的路由,但是被app中的路由給覆蓋了。
5、附:app.verbs和app.use的路由路徑區別:
先看一段測試代碼:
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
輸出結果:test1
輸入url: localhost:4000/hello
輸出結果:test2
結論:app.get掛載‘/’的路由只響應跟'/'精確匹配的GET請求。 而app.use掛載的'/'的路由響應所有以'/' 為起始路由的路由,且不限制HTTP訪問的方法。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。