Koa中間件使用之koa-router

Koa-router 是 koa 的一個路由中間件,它可以將請求的URL和方法(如:GET 、 POST 、 PUTDELETE 等) 匹配到對應的響應程序或頁面。本文將介紹 koa-router 基本配置、使用以及一些參考筆記。

基本配置

創建Koa應用

下面的代碼創建了一個koa web服務,監聽了3000端口,如果訪問 http://localhost:3000/ 將返回 Not Found ,這是因為代碼沒有對請求做任何響應。后面將使用 koa-router 在這個基礎上進行修改,使其支持不同的路由匹配。

// app.js

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

const app = new Koa(); // 創建koa應用

// 啟動服務監聽本地3000端口
app.listen(3000, () => {
    console.log('應用已經啟動,http://localhost:3000');
})

安裝koa-router

$ npm install koa-router --save

使用koa-router

首先,使用 require() 引入 koa-router ,并且對其實例化(支持傳遞參數),然后使用獲取到的路由實例 router 設置一個路徑,將 '/' 匹配到相應邏輯,返回一段HTML 。接著還需要分別調用 router.routes()router.allowedMethods() 來得到兩個中間件,并且調用 app.use() 使用這兩個中間件:

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

const app = new Koa(); // 創建koa應用
const router = new Router(); // 創建路由,支持傳遞參數

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

// 調用router.routes()來組裝匹配好的路由,返回一個合并好的中間件
// 調用router.allowedMethods()獲得一個中間件,當發送了不符合的請求時,會返回 `405 Method Not Allowed` 或 `501 Not Implemented`
app.use(router.routes());
app.use(router.allowedMethods({ 
    // throw: true, // 拋出錯誤,代替設置響應頭狀態
    // notImplemented: () => '不支持當前請求所需要的功能',
    // methodNotAllowed: () => '不支持的請求方式'
}));

// 啟動服務監聽本地3000端口
app.listen(3000, () => {
    console.log('應用已經啟動,http://localhost:3000');
})

使用

不同請求方式

Koa-router 請求方式: getput 、 post 、 patchdeletedel ,而使用方法就是 router.方式() ,比如 router.get()router.post() 。而 router.all() 會匹配所有的請求方法。

當 URL 匹配成功,router 就會執行對應的中間件來對請求進行處理,下面是使用示例:

// ...

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

// ...

從請求參數取值

有些時候需要從請求URL上獲取特定參數,主要分為兩類: paramsquery 。 這兩種參數獲取的方式如下:

params參數

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

query參數

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

路由使用中間件

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

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

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

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

app.use(router.routes());

設置路由前綴

可以通過調用 router.prefix(prefix) 來設置路由的前綴,也可以通過實例化路由的時候傳遞參數設置路由的前綴,比如在 RESTful 接口里面,往往會為接口設置一個 api 前綴,如:

router.prefix('/api')

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

當然也支持設置參數:

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

路由嵌套

有時路由涉及到很多業務模塊,可能需要對模塊進行拆分和嵌套,koa-router 提供了路由嵌套的功能,使用也很簡單,就是創建兩個 Router 實例,然后將被嵌套的模塊路由作為父級路由的中間件使用:

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());

拆分路由

通過路由嵌套可以對路由進行拆分,不同的模塊使用不同的文件,如下面的示例:

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

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

const app = new Koa(); // 創建koa應用

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

// 啟動服務監聽本地3000端口
app.listen(3000, () => {
    console.log('應用已經啟動,http://localhost:3000');
})

router/user.js 設置了 user 模塊的路由,并且導出:

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

const router = new Router();

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

module.exports = router;

router/index.js 導出了整個路由模塊:

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

const router = new Router();

// 指定一個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"

通過 ctx._matchedRoute 獲得匹配的路由,通過 ctx._matchedRouteName 獲得匹配的路由名。

設置多個中間件

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]) 可以對路由進行重定向,例子:

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

等價于:

router.all('/login', ctx => {
  ctx.redirect('/sign-in');
  ctx.status = 301;
});
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374