Koa中間件使用之koa-router

Koa-router 是 koa 的一個(gè)路由中間件冈在,它可以將請(qǐng)求的URL和方法(如:GETPOST 按摘、 PUT 包券、 DELETE 等) 匹配到對(duì)應(yīng)的響應(yīng)程序或頁(yè)面。本文將介紹 koa-router 基本配置炫贤、使用以及一些參考筆記丧靡。

基本配置

創(chuàng)建Koa應(yīng)用

下面的代碼創(chuàng)建了一個(gè)koa web服務(wù)谜慌,監(jiān)聽(tīng)了3000端口励负,如果訪(fǎng)問(wèn) http://localhost:3000/ 將返回 Not Found 梯嗽,這是因?yàn)榇a沒(méi)有對(duì)請(qǐng)求做任何響應(yīng)。后面將使用 koa-router 在這個(gè)基礎(chǔ)上進(jìn)行修改,使其支持不同的路由匹配亮元。

// app.js

const Koa = require('koa'); // 引入koa

const app = new Koa(); // 創(chuàng)建koa應(yīng)用

// 啟動(dòng)服務(wù)監(jiān)聽(tīng)本地3000端口
app.listen(3000, () => {
    console.log('應(yīng)用已經(jīng)啟動(dòng)猛计,http://localhost:3000');
})

安裝koa-router

$ npm install koa-router --save

使用koa-router

首先,使用 require() 引入 koa-router 爆捞,并且對(duì)其實(shí)例化(支持傳遞參數(shù))奉瘤,然后使用獲取到的路由實(shí)例 router 設(shè)置一個(gè)路徑,將 '/' 匹配到相應(yīng)邏輯煮甥,返回一段HTML 盗温。接著還需要分別調(diào)用 router.routes()router.allowedMethods() 來(lái)得到兩個(gè)中間件,并且調(diào)用 app.use() 使用這兩個(gè)中間件:

const Koa = require('koa'); // 引入koa
const Router = require('koa-router'); // 引入koa-router

const app = new Koa(); // 創(chuàng)建koa應(yīng)用
const router = new Router(); // 創(chuàng)建路由成肘,支持傳遞參數(shù)

// 指定一個(gè)url匹配
router.get('/', async (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h1>hello world!</h1>';
})

// 調(diào)用router.routes()來(lái)組裝匹配好的路由卖局,返回一個(gè)合并好的中間件
// 調(diào)用router.allowedMethods()獲得一個(gè)中間件,當(dāng)發(fā)送了不符合的請(qǐng)求時(shí)艇劫,會(huì)返回 `405 Method Not Allowed` 或 `501 Not Implemented`
app.use(router.routes());
app.use(router.allowedMethods({ 
    // throw: true, // 拋出錯(cuò)誤吼驶,代替設(shè)置響應(yīng)頭狀態(tài)
    // notImplemented: () => '不支持當(dāng)前請(qǐng)求所需要的功能',
    // methodNotAllowed: () => '不支持的請(qǐng)求方式'
}));

// 啟動(dòng)服務(wù)監(jiān)聽(tīng)本地3000端口
app.listen(3000, () => {
    console.log('應(yīng)用已經(jīng)啟動(dòng)惩激,http://localhost:3000');
})

使用

不同請(qǐng)求方式

Koa-router 請(qǐng)求方式: get 店煞、 putpost 风钻、 patch 顷蟀、 deletedel 骡技,而使用方法就是 router.方式() 鸣个,比如 router.get()router.post() 。而 router.all() 會(huì)匹配所有的請(qǐng)求方法布朦。

當(dāng) URL 匹配成功囤萤,router 就會(huì)執(zhí)行對(duì)應(yīng)的中間件來(lái)對(duì)請(qǐng)求進(jìn)行處理,下面是使用示例:

// ...

// 指定一個(gè)url匹配
router.get('/', async (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h1>hello world!</h1>';
})
    .get("/users", async (ctx) => {
        ctx.body = '獲取用戶(hù)列表';
    })
    .get("/users/:id", async (ctx) => {
        const { id } = ctx.params
        ctx.body = `獲取id為${id}的用戶(hù)`;
    })
    .post("/users", async (ctx) => {
        ctx.body = `創(chuàng)建用戶(hù)`;
    })
    .put("/users/:id", async (ctx) => {
        const { id } = ctx.params
        ctx.body = `修改id為${id}的用戶(hù)`;
    })
    .del("/users/:id", async (ctx) => {
        const { id } = ctx.params
        ctx.body = `刪除id為${id}的用戶(hù)`;
    })
    .all("/users/:id", async (ctx) => {
        ctx.body = ctx.params;
    });

// ...

從請(qǐng)求參數(shù)取值

有些時(shí)候需要從請(qǐng)求URL上獲取特定參數(shù)是趴,主要分為兩類(lèi): paramsquery 涛舍。 這兩種參數(shù)獲取的方式如下:

params參數(shù)

router.get('/:category/:title', (ctx, next) => {
  console.log(ctx.params);
  // => { category: 'programming', title: 'how-to-node' }
});

query參數(shù)

router.get("/users", async (ctx) => {
    console.log('查詢(xún)參數(shù)', ctx.query);
    ctx.body = '獲取用戶(hù)列表';
})

路由使用中間件

router 還支持使用中間件,并且可以針對(duì)特定的URL或者多個(gè)URL使用中間件:

// 先后設(shè)置兩個(gè)中間件
router
  .use(session())
  .use(authorize());

// 給指定地址使用中間件
router.use('/users', userAuth());

// 給數(shù)組里面的地址使用中間件
router.use(['/users', '/admin'], userAuth());

app.use(router.routes());

設(shè)置路由前綴

可以通過(guò)調(diào)用 router.prefix(prefix) 來(lái)設(shè)置路由的前綴唆途,也可以通過(guò)實(shí)例化路由的時(shí)候傳遞參數(shù)設(shè)置路由的前綴富雅,比如在 RESTful 接口里面,往往會(huì)為接口設(shè)置一個(gè) api 前綴肛搬,如:

router.prefix('/api')

// 或者
const router = new Router({
   prefix: '/api' 
})

當(dāng)然也支持設(shè)置參數(shù):

router.prefix('/路徑/:參數(shù)')

路由嵌套

有時(shí)路由涉及到很多業(yè)務(wù)模塊没佑,可能需要對(duì)模塊進(jìn)行拆分和嵌套,koa-router 提供了路由嵌套的功能温赔,使用也很簡(jiǎn)單蛤奢,就是創(chuàng)建兩個(gè) Router 實(shí)例,然后將被嵌套的模塊路由作為父級(jí)路由的中間件使用:

var forums = new Router();
var posts = new Router();

posts.get('/', (ctx, next) => {...});
posts.get('/:pid', (ctx, next) => {...});
forums.use('/forums/:fid/posts', posts.routes(), posts.allowedMethods());

// responds to "/forums/123/posts" and "/forums/123/posts/123"
app.use(forums.routes());

拆分路由

通過(guò)路由嵌套可以對(duì)路由進(jìn)行拆分,不同的模塊使用不同的文件远剩,如下面的示例:

app.js 只引入路由入口文件

const Koa = require('koa'); // 引入koa
+ const router = require('./router');

const app = new Koa(); // 創(chuàng)建koa應(yīng)用

+ app.use(router.routes());
+ app.use(router.allowedMethods());

// 啟動(dòng)服務(wù)監(jiān)聽(tīng)本地3000端口
app.listen(3000, () => {
    console.log('應(yīng)用已經(jīng)啟動(dòng)扣溺,http://localhost:3000');
})

router/user.js 設(shè)置了 user 模塊的路由,并且導(dǎo)出:

const Router = require('koa-router');

const router = new Router();

router.get("/", async (ctx) => {
    console.log('查詢(xún)參數(shù)', ctx.query);
    ctx.body = '獲取用戶(hù)列表';
})
    .get("/:id", async (ctx) => {
        const { id } = ctx.params
        ctx.body = `獲取id為${id}的用戶(hù)`;
    })
    .post("/", async (ctx) => {
        ctx.body = `創(chuàng)建用戶(hù)`;
    })
    .put("/:id", async (ctx) => {
        const { id } = ctx.params
        ctx.body = `修改id為${id}的用戶(hù)`;
    })
    .del("/:id", async (ctx) => {
        const { id } = ctx.params
        ctx.body = `刪除id為${id}的用戶(hù)`;
    })
    .all("/users/:id", async (ctx) => {
        ctx.body = ctx.params;
    });

module.exports = router;

router/index.js 導(dǎo)出了整個(gè)路由模塊:

const Router = require('koa-router');
const user = require('./user');

const router = new Router();

// 指定一個(gè)url匹配
router.get('/', async (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h1>hello world!</h1>';
})

router.use('/user', user.routes(), user.allowedMethods());

module.exports = router;

筆記

命名路由

router.get('user', '/users/:id', (ctx, next) => {
 // ...
});

router.url('user', 3);
// => "/users/3"

通過(guò) ctx._matchedRoute 獲得匹配的路由瓜晤,通過(guò) ctx._matchedRouteName 獲得匹配的路由名锥余。

設(shè)置多個(gè)中間件

router.get(
  '/users/:id',
  (ctx, next) => {
    return User.findOne(ctx.params.id).then(function(user) {
      ctx.user = user;
      next();
    });
  },
  ctx => {
    console.log(ctx.user);
    // => { id: 17, name: "Alex" }
  }
);

路由重定向

使用 router.redirect(source, destination, [code]) 可以對(duì)路由進(jìn)行重定向,例子:

router.redirect('/login', 'sign-in');

等價(jià)于:

router.all('/login', ctx => {
  ctx.redirect('/sign-in');
  ctx.status = 301;
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痢掠,一起剝皮案震驚了整個(gè)濱河市驱犹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌足画,老刑警劉巖雄驹,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異淹辞,居然都是意外死亡医舆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)象缀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蔬将,“玉大人,你說(shuō)我怎么就攤上這事央星∠蓟常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵莉给,是天一觀(guān)的道長(zhǎng)毙石。 經(jīng)常有香客問(wèn)我,道長(zhǎng)颓遏,這世上最難降的妖魔是什么徐矩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮叁幢,結(jié)果婚禮上滤灯,老公的妹妹穿的比我還像新娘。我一直安慰自己遥皂,他們只是感情好力喷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著演训,像睡著了一般弟孟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上样悟,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天拂募,我揣著相機(jī)與錄音庭猩,去河邊找鬼。 笑死陈症,一個(gè)胖子當(dāng)著我的面吹牛蔼水,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播录肯,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼趴腋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了论咏?” 一聲冷哼從身側(cè)響起优炬,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厅贪,沒(méi)想到半個(gè)月后蠢护,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡养涮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年葵硕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贯吓。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懈凹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宣决,到底是詐尸還是另有隱情蘸劈,我是刑警寧澤昏苏,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布尊沸,位于F島的核電站,受9級(jí)特大地震影響贤惯,放射性物質(zhì)發(fā)生泄漏洼专。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一孵构、第九天 我趴在偏房一處隱蔽的房頂上張望屁商。 院中可真熱鬧,春花似錦颈墅、人聲如沸蜡镶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)官还。三九已至,卻和暖如春毒坛,著一層夾襖步出監(jiān)牢的瞬間望伦,已是汗流浹背林说。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屯伞,地道東北人腿箩。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像劣摇,于是被迫代替她去往敵國(guó)和親珠移。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353