本篇文章就來給大家介紹一些你不知道的Node express 路由使用技巧,希望對大家有所幫助!
node.js極速入門課程:進(jìn)入學(xué)習(xí)
在之前的文章中我們說到express
為我們提供的最強(qiáng)大的能力就是一套超級簡單且強(qiáng)大的路由系統(tǒng),那到底什么是路由呢?
路由是指如何定義應(yīng)用的端點(diǎn)(URIs
)以及如何響應(yīng)客戶端的請求。【相關(guān)教程推薦:nodejs視頻教程】
路由是由一個 URI
、HTTP
請求(GET
、POST
等)和若干個句柄組成,它的結(jié)構(gòu)如下:
app.METHOD(path, [callback...], callback)
app
是express
對象的一個實(shí)例METHOD
是一個HTTP
請求方法path
是服務(wù)器上的路徑callback
是當(dāng)路由匹配時要執(zhí)行的函數(shù)
這篇文章我們將深入去學(xué)習(xí)express
的路由系統(tǒng),讓我們開始吧!
1、基礎(chǔ)路由
先定義一個基礎(chǔ)的路由:
const express = require("express"); // app對象 const app = express(); app.get("/", (req, res) => { res.send("匹配/"); });
上面的例子表示的是一個路徑為/
的GET
請求路由,即當(dāng)用戶使用GET
請求訪問/
時會執(zhí)行(req, res) =>{res.send("匹配/");}
函數(shù)。
路由路徑和請求方法一起定義了請求的端點(diǎn):
// 匹配 / 路徑的get請求 app.get("/", (req, res) => { res.send("匹配 / 路徑的get請求"); }); // 匹配 / 路徑的post請求 app.post("/", (req, res) => { res.send("匹配 / 路徑的post請求"); }); // 匹配 / 路徑的put請求 app.put("/", (req, res) => { res.send("匹配 / 路徑的put請求"); }); // 匹配 / 路徑的delete請求 app.delete("/", (req, res) => { res.send("匹配 / 路徑的delete請求"); });
觀察上面的路由你會發(fā)現(xiàn)它們的請求路徑是一樣的,只是請求方式不同,這是允許的,因?yàn)橄嗤窂降恼埱笾灰埱蠓椒ú煌詈缶蜁M(jìn)入到不同的處理函數(shù)中。
這種風(fēng)格的寫法稱為RES Tful,在開發(fā)API
接口中比較推薦使用。
RES Tful特點(diǎn)包括:
1、每一個URI代表1種資源;
2、客戶端使用GET、POST、PUT、DELETE4個表示操作方式的動詞對服務(wù)端資源進(jìn)行操作:GET用來獲取資源,POST用來新建資源(也可以用于更新資源),PUT用來更新資源,DELETE用來刪除資源;
3、通過操作資源的表現(xiàn)形式來操作資源;
4、資源的表現(xiàn)形式是XML或者HTML;
5、客戶端與服務(wù)端之間的交互在請求之間是無狀態(tài)的,從客戶端到服務(wù)端的每個請求都必須包含理解請求所必需的信息。
2、路徑匹配
express
的路由可以進(jìn)行模糊匹配,這是因?yàn)?code>express的路由路徑可以是字符串、字符串模式或者正則表達(dá)式。
字符串匹配
當(dāng)路徑使用字符串時進(jìn)行的就是普通的匹配,路徑是什么就匹配什么:
// 匹配根路徑的請求 app.get('/', function (req, res) { res.send('root'); }); // 匹配 /about 路徑的請求 app.get('/about', function (req, res) { res.send('about'); });
字符串模式匹配
在字符串的基礎(chǔ)上添加一些額外的語法(如下)稱為字符串模式。
?
表示可選:id
表示使用id
字段占位+
表示可出現(xiàn)多次(至少一次)*
表示任意
使用字符串模式的路徑:
// 匹配 /acd 和 /abcd:?前邊的字符代表可選 app.get("/ab?cd", function (req, res) { res.send("ok"); }); // 匹配 /ab/*******::id代表占位,這個id是自定義的(你也可以寫:aa,:bb等),之后能用來獲取/ab/后的參數(shù) app.get("/ab/:id", function (req, res) { res.send("/ab/*******"); }); // 匹配 /abcd,/abbcd,/abbbcd等,+號前的字符可以連續(xù)出現(xiàn)多個 app.get("/ab+cd", function (req, res) { res.send("/ab+cd"); }); // 匹配 /abcd,/abxcd,/abBIHIHIcd,/ab1456cd等,*號代表任意字符 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"); });
正則匹配
express
路由匹配中最強(qiáng)大的一點(diǎn)就是它能夠根據(jù)正則來進(jìn)行匹配(雖然這在開發(fā)中用處不多)。
使用正則表達(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$/"); });
3、處理函數(shù)
可以為請求處理提供多個回調(diào)函數(shù),其行為類似 中間件。
唯一的區(qū)別是這些回調(diào)函數(shù)有可能調(diào)用 next('route')
或next('router')
方法而略過其他路由回調(diào)函數(shù)。
多函數(shù)
使用多個回調(diào)函數(shù)處理路由(記得指定 next
對象):
// 直接添加多個回調(diào)函數(shù) app.get( "/", (req, res, next) => { console.log("第一個執(zhí)行"); next(); }, (req, res, next) => { console.log("第二個執(zhí)行"); next(); // next('route') next中添加route參數(shù)時會跳過之后的回調(diào)函數(shù) }, (req, res) => { console.log("最后一個執(zhí)行"); // 返回數(shù)據(jù)內(nèi)容 res.send("home"); } );
調(diào)用
next()
就是意味著放行去執(zhí)行下一個函數(shù)
注意:向客戶端返回信息的操作(
res.send
)要放到最后一個處理函數(shù)中,如果放到前面則會阻止后面處理函數(shù)的運(yùn)行(這樣的話,后面定義的處理函數(shù)有什么意義?),并且當(dāng)res.send
與next()
處于一個處理函數(shù)中時會報錯
函數(shù)數(shù)組
還可以以數(shù)組的形式添加回調(diào)函數(shù):
const f1 = (req, res, next) => { console.log("第一個執(zhí)行"); next(); }; const f2 = (req, res, next) => { console.log("第二個執(zhí)行"); next(); }; const f3 = (req, res) => { console.log("最后一個執(zhí)行"); res.send("login"); }; app.get("/login", [f1, f2, f3]);
混合使用
混合使用函數(shù)和函數(shù)數(shù)組處理路由:
const fn1 = function (req, res, next) { console.log("fn1"); next(); }; var fn2 = function (req, res, next) { console.log("fn2"); next(); }; app.get( "/about", [fn1, fn2], function (req, res, next) { console.log("fn3"); next(); }, function (req, res) { res.send("about"); } );
應(yīng)用
可以利用該機(jī)制為路由定義前提條件,如果在現(xiàn)有路徑上繼續(xù)執(zhí)行沒有意義,則可將控制權(quán)交給剩下的路徑。
例如在開發(fā)中經(jīng)常使用到的token
驗(yàn)證,假如現(xiàn)在共有三個接口:
/login
用戶登錄的接口,不需要token
驗(yàn)證/home
只有token
驗(yàn)證通過才能訪問/user
只有token
驗(yàn)證通過才能訪問
這時我們就可以這樣開發(fā):
// 不需要token驗(yàn)證 app.get("/login", (req, res) => { res.send("登錄"); }); // 驗(yàn)證token的函數(shù)(可插拔,可復(fù)用的中間件) const isToken = (req, res, next) => { // 驗(yàn)證token是否過期,isVaild代表token是否有效 // 一些操作 const isVaild = true; // 假設(shè)驗(yàn)證通過 if (isVaild) { // 驗(yàn)證通過了就調(diào)用next向下執(zhí)行 console.log("token驗(yàn)證通過"); // 如果想要在回調(diào)函數(shù)之間傳遞數(shù)據(jù),我們可以選擇將數(shù)據(jù)掛載到res參數(shù)上 res.ailjx = "海底燒烤店ai"; next(); } else { // 返回錯誤 // 這里res.send后后面的回調(diào)函數(shù)就不會再執(zhí)行了 res.send("token驗(yàn)證失??!"); } }; // 只有token有效時才生效的接口 app.get("/home", [isToken], (req, res) => { // 返回數(shù)據(jù)內(nèi)容 // res.ailjx:獲取驗(yàn)證token的函數(shù)在res上綁定的數(shù)據(jù) res.send("home" + res.ailjx); }); app.get("/user", [isToken], (req, res) => { // 返回數(shù)據(jù)內(nèi)容 res.send("user"); });
上面我們將驗(yàn)證token
的函數(shù)抽離了出去,之后在需要該函數(shù)的路由中直接引用即可,這就避免了我們需要在每個路由中都寫一遍token驗(yàn)證的繁瑣過程。
4、路由級中間件
上面我們定義的路由都是直接綁定到了app
對象上,這稱為應(yīng)用級中間件。
應(yīng)用級中間件綁定到
app
對象 使用app.use()
和app.METHOD()
, 其中,METHOD
是需要處理的HTTP
請求的方法,例如get
,put
,post
等等,全部小寫。
當(dāng)我們的路由過多時,這勢必會變得難以維護(hù),于是就出現(xiàn)了路由級中間件用來將我們的路由抽離出來,路由級中間件和應(yīng)用級中間件一樣,只是它綁定的對象為 express.Router()
。
// apiRouter.js const express = require("express"); // router對象 const router = express.Router(); // 路由級別中間件:api路由 router.get("/home", (req, res) => { res.send({ list: [1, 2, 3, 4], }); }); router.get("/about", (req, res) => { res.send({ name: "ailjx", age: 18, }); }); // 導(dǎo)出 module.exports = router;
// server.js const express = require("express"); const app = express(); // 導(dǎo)入api路由 const apiRouter = require("./route/apiRouter"); // 使用app.use將路由掛載至應(yīng)用 app.use(apiRouter); app.listen(3000, () => { console.log("start"); });
上面我們使用路由級中間件定義路由,之后通過模塊化導(dǎo)入的形式將路由導(dǎo)入,再使用app.use
將路由掛載至應(yīng)用,這種代碼結(jié)構(gòu)就使得整個項(xiàng)目變得更加容易維護(hù)。
在掛載路由時我們也可以指定一級路徑:
// 如果use具有第一個路徑參數(shù),如下面的/api,則/api相當(dāng)于是一級路徑,apiRouter里的路徑就相當(dāng)于二級路徑了 app.use("/api", apiRouter);
這樣掛載后的路由訪問時就需要加上/api
,如:
/api/home
/api/about
結(jié)語
本篇文章詳細(xì)介紹了express
路由的使用,并由此引入路由級中間件的使用(關(guān)于express中間件
博主之后會出單獨(dú)的文章進(jìn)行講解)。