Koa-router 是 koa 的一個(gè)路由中間件冈在,它可以將請(qǐng)求的URL和方法(如:GET
、 POST
按摘、 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
店煞、 put
、 post
风钻、 patch
顷蟀、 delete
、 del
骡技,而使用方法就是 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): params
和 query
涛舍。 這兩種參數(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;
});