- Node.js+Webpack開發實戰
- 夏磊
- 1827字
- 2021-03-26 21:53:50
4.3 路由
路由是指應用程序如何根據指定的路由路徑和指定的HTTP請求方法(GET和POST等)來處理請求。
在Express應用中,每個路由可以有一個或多個處理函數,這些函數會在路由匹配時執行。
路由采用以下結構定義:
app.METHOD(PATH, HANDLER)
· app:應用實例。
· METHOD:小寫get和post等的HTTP請求方法。
· PATH:路由路徑。
· HANDLER:路由匹配時執行的函數。
4.3.1 路由方法
路由方法是從HTTP方法派生的,以下是GET和POST方法定義路由的示例:
app.get('/', (req, resp) => { resp.send('GET請求'); }); app.post('/', (req, resp) => { resp.send('POST請求'); });
Express支持所有的HTTP請求方法:
· get
· post
· head
· options
· delete
· put
· patch
如果需要使用一個路由來處理所有的請求方法,則可以調用app.all():
app.all('/', (req, resp) => { resp.send('請求首頁'); });
4.3.2 路由路徑
路由路徑結合請求方法,定義了可以發出請求的地址。路由路徑可以是字符串、字符串模式或正則表達式。
查詢字符串(一般稱為GET參數)不是路由路徑的一部分。
(1)以下是一些基于字符串的路由路徑的示例。
以下路由路徑會將請求匹配到根路由/:
app.get('/', (req, resp) => { resp.send('主頁'); });
以下路由路徑會將請求匹配到/about:
app.get('/about', (req, resp) => { resp.send('關于頁面'); });
以下路由路徑可以將請求匹配到/random.txt文件:
app.get('/random.txt', (req, resp) => { resp.send('random.txt'); });
字符串模式可以“認為”是正則表達式的子集,支持部分正則表達式語法。
(2)以下是一些基于字符串模式的路由路徑示例。
以下路由路徑將與/acd和/abcd相匹配。“?”號在正則表達式中代表“至多一個”,示例代碼中就是匹配“至多一個b”:
app.get('/ab?cd', (req, resp) => { resp.send('ab?cd'); });
以下路由路徑將與/abcd、/abbcd、/abbbcd等等相匹配。+在正則表達式中代表“至少一個”,示例代碼中就是匹配“至少一個b”:
app.get('/ab+cd', (req, resp) => { resp.send('ab+cd'); });
以下路由路徑將與/abcd和/ad相匹配。()在正則表達式中代表分組,示例代碼中就是“要么有一個bc,要么沒有bc”:
app.get('/a(bc)?d', (req, resp) => { resp.send('a(bc)?d'); });
(3)以下是一些基于正則表達式的路由路徑示例。
以下路由路徑將與任何帶有user的請求鏈接相匹配:
app.get(/user/, (req, resp) => { resp.send('/user/'); });
以下路由路徑將嚴格與/admin匹配:
app.get(/^\/admin$/, (req, resp) => { resp.send('/^\/admin$/'); });
4.3.3 路由參數
路由參數用于捕獲URL中各位置的值。捕獲的值將填充到req.params對象中,并將路由路徑中指定的route參數名稱作為req.params對象的鍵。
app.get('/users/:userId/timelines/:timelineId', (req, resp) => { resp.json(req.params); });
訪問:http://localhost:8080/users/1/timelines/1,將得到以下響應:
{ "userId": "1", "timelineId": "1" }
路由參數的名稱必須由[A-Za-z0-9_](也就是大小寫字母、數字、下畫線)組成。
字符串和字符串模式路由路徑中的中劃線“-”和點“.”無特殊意義,Express按照字面意思處理這兩個字符。因為在正則表達式中這兩個字符有特殊意義,特此說明以防止混淆。
app.get('/users/:firstName.:lastName', (req, resp) => { resp.json(req.params); });
訪問http://localhost:8080/users/lei.xia,將得到以下響應:
{ "firstName": "lei", "lastName": "xia" }
在/users/:userId/timelines/:timelineId例子中,預期匹配的是數字ID類型的參數,但是由于沒有類型限制,字符串形式的參數也會匹配到。如果不限制參數類型,容易引發類型問題。
在路由參數后使用正則表達式可以限制參數類型,不滿足類型的參數無法匹配該路由。
比如上述例子中,我們限制userId和timelineId為數字類型,可以使用以下代碼:
app.get('/users/:userId(\\d+)/timelines/:timelineId(\\d+)', (req, resp) => { resp.json(req.params); });
訪問http://localhost:8080/users/1/timelines/1,將得到以下響應:
{ "userId": "1", "timelineId": "1" }
訪問http://localhost:8080/users/1a/timelines/1,將得到以下響應:
Cannot GET /users/1a/timelines/1
因為1a與(\d+)不匹配。
4.3.4 路由函數
路由方法和路由路徑匹配之后就會執行對應的路由函數,路由函數的簽名如下:
function(request, response, next)
· request Express:請求對象。
· response Express:響應對象。
· next:匹配的下一個路由函數(可選參數)。
1.單個路由函數
最簡單的路由函數,對請求處理后發出響應,結束本次請求處理。
app.get('/', (req, resp) => { resp.send('/'); });
2.多個路由函數
對于同一個路由,可以定義多個路由函數來處理,每個路由函數做一項工作。
不要忘記next()方法的調用,即使定義多個路由函數,只要第一個函數未調用next(),后續的路由函數都不會執行。
app.get('/', (req, resp, next) => { console.log(`${req.method} ${req.path}`); next(); }, (req, resp) => { resp.send('首頁'); });
如下示例定義了兩個路由函數,第一個函數打印了當前請求方法和請求路徑,然后調用next()執行下一個路由函數以響應此次請求。在上面的示例中,訪問首頁時終端會輸出“GET /”,之后會向客戶端輸出“首頁”字樣。Express中還可以使用函數數組來定義多個路由函數,上述例子的另外一種寫法如下,兩種寫法效果是一樣的。
function logger(req, resp, next) { console.log(`${req.method} ${req.path}`); next(); } function home(req, resp) { resp.send('首頁'); } // 設置路由 app.get('/', [logger, home]);
3.公共路由路徑
如果多個路由有同樣的路由路徑,只是請求方法不同,若分別為每種方法定義一次路由,則不利于模塊化。
app.get('/user/login', (req, resp) => { resp.send('登錄頁面'); }); app.post('/user/login', (req, resp) => { resp.send('登錄處理'); });
針對這種典型場景,Express提供了app.router()來處理:

4.模塊化的路由
使用Express提供的Router對象可以創建模塊化的對象,實現路由和入口JS的解耦。使用模塊化的路由有以下優點:
· 便于維護。
· 統一的路由前綴。
· 模塊化。
user.js用戶相關路由:
const express = require('express'); const router = express.Router(); router.get('/login', (req, resp) => { resp.send('登錄'); }); router.get('/register', (req, resp) => { resp.send('注冊'); }); // 導出路由對象 module.exports = router;
timeline.js動態相關路由:
const express = require('express'); const router = express.Router(); router.get('/list', (req, resp) => { resp.send('動態列表'); }); // 導出路由對象 module.exports = router;
index.js入口文件:

上述例子最終會生成以下路由:
· GET /user/login。
· GET /user/register。
· GET /timeline/list。