1.前言
我们想构建一个对外提供的 Api 接口的 Web server 肯定是离不开 路由的,路由可以标记每个 Api 的地址,打个比方,你是地主家的儿子,你们家有几百套房子,那需要一个有个本子(路由)记录每套房子(资源)在哪,不然收租都找不大地方,对应下就是,路由就是这个记录的索引,方便我们找到需要的资源,服务。
所以这章我们来学习下路由的使用,包含以下 2 个方面:
- 原生路由的使用
- koa-router 中间件
一般我们写完接口肯定是需要测试下的,假设我们写一个页面来调试每个请求肯定是很麻烦的,一般我们会用 Postman
这个工具来调试接口,大家有时间也可以看看Postman使用。
2. 原生路由
比如我们现在要实现用户管理的增删改查接口编写,用 Koa 写会有下面一段代码。
/*
* @Author: hucheng
* @Date: 2020-06-22 06:41:21
* @Description: here is des
*/
const Koa = require('koa')
const fs = require('fs')
const app = new Koa()
const storeArray = []
app.use( async ( ctx ) => {
let url = ctx.request.url
let html = await route( url )
ctx.body = html
})
async function route( url ) {
let view = '404.html'
switch ( url ) {
case '/':
view = 'index.html'
break
case '/index':
view = 'index.html'
break
case '/todo':
view = 'todo.html'
break
default:
break
}
let html = await render( view )
return html
}
app.use( async ( ctx ) => {
let url = ctx.request.url
let data = await route( url )
ctx.body = data
})
app.listen(3000, () => {
console.log(' starting at port 3000')
})
function render( page ) {
return new Promise(( resolve, reject ) => {
let viewUrl = `./view/${page}`
fs.readFile(viewUrl, "binary", ( err, data ) => {
if ( err ) {
reject( err )
} else {
resolve( data )
}
})
})
}
这里我就实现了一个简单的路由,然后问题来了,真实的项目里面,路由肯定是非常多的,上面这种处理方式,写到最后估计会疯掉,代码可读性也比较差,如是社区就出来个中间件 koa-router
来定义一套路由的处理规范。
Demo 对应github连接,大家可以拉下来本地跑起来,体会下。
3. koa-router
3.1 简单使用
把上面的例子,我们用 koa-router 改下一遍。
/*
* @Author: hucheng
* @Date: 2020-06-22 06:41:21
* @Description: here is des
*/
const Koa = require('koa')
const Router = require('koa-router');
const fs = require('fs')
const app = new Koa()
const router = new Router();
router.get('/', async (ctx, next) => {
ctx.body = await render('index.html')
});
router.get('/index', async (ctx, next) => {
ctx.body = await render('index.html')
});
router.get('/todo', async (ctx, next) => {
ctx.body = await render('todo.html')
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log(' starting at port 3000')
})
function render( page ) {
return new Promise(( resolve, reject ) => {
let viewUrl = `./view/${page}`
fs.readFile(viewUrl, "binary", ( err, data ) => {
if ( err ) {
reject( err )
} else {
resolve( data )
}
})
})
}
大家看到我们这里 我们用 koa-router 重写了一遍,代码逻辑是不是清晰了很多,可读行大大增强。
在实际业务开发过程中,我们都是用 koa-router 来做 请求的url管理,接下来讲讲常用的方法。
3.2常用方法
3.2.1 url 带参数
比如有这样的一个场景,我们需要查询某个用户 Id 为 21 的详情信息,请求的url 可以有下面2种组织方式。
第一种: http://localhost:3000/user/detail?userId=21
第二种: http://localhost:3000/user/detail/21
那在实际开发过程中,我们更推荐第二种,这种看起来更加清晰,koa-router 支持这种带参数的方式,设置如下:
router
.get('/user/detail/:id', (ctx, next) => {
const id = ctx.params.id
ctx.body = 'Hello World!';
})
3.2.2 http 常见method
那我们在开发过程中经常会有 get,post,put 等请求的 method ,koa-router 都有对应设置。
router.get('/', (ctx, next) => {
ctx.body = 'Hello World!';
})
router.post('/users', (ctx, next) => {
// ...
})
router.put('/users/:id', (ctx, next) => {
// ...
})
router.del('/users/:id', (ctx, next) => {
// ...
})
以上就是 koa-router常见使用,在平常开发中,koa-router
会贯穿整个开发流程,这里我们简单的梳理下,它更多的使用是和 request 上下文结合起来使用。
3.3 路由配合中间件使用
有时候我们写的接口,用户登录后,返回个 Token 给前端页面,后面其他请求 都要校验 Request header 是否带了 这个Token,来确保权限,那这个就需要配和中间件了。
login.js
// login.js
function login(ctx) {
const token = '121212-45dfgffgfg'
ctx.res.header.token = token
ctx.body = {
token: token
}
}
module.exports = {
login
}
auth.js ,是认证中间件,中间件我们一般放到 midware 这个目录下面。
// /midware/auth.js
async function auth(ctx, next) {
const whiteList = ['/', '/index', '/login'] // 这里我们标记白名单,不走认证的
if(!whiteList.includes(ctx.request.path) && !ctx.request.headers['token']) {
ctx.throw(401, '没有token,请登录')
}
await next()
}
module.exports = auth
// router.js
const { login } = require('./controller/login')
const auth = require('./midware/auth')
const {list, detail} = require('./controller/user')
router.use(auth)
router.get('/', async (ctx, next) => {
ctx.body = await render('index.html')
});
router.post('/login', login);
router.get('/index', async (ctx, next) => {
ctx.body = await render('index.html')
});
router.get('/todo', async (ctx, next) => {
ctx.body = await render('todo.html')
});
启动项目访问下 http://localhost:3000/todo。
有的时候我们需要对某个接口做些处理,比如有一个接口有包含文件,那我们肯定不想每个这样的接口都处理下文件,这样单独的接口可以用单独的中间件,例如:
// 单独的路由处理用到中间件
router.use('/users', operateFile());
// 多个接口都要用到某个中间件
router.use(['/users', '/admin'], operateFile());
app.use(router.routes());
3.4 常用规范
一般在实际开发过程中,我们会把 路由相关的,会归总放到 router.js,这样统一管理,代码结构性好, 可读性强。
// 目录结构
|__koa-router-demo
|
app.js
|
router.js
|
constroller // constroller层
|
midware // 这个放中间件
app.js
const Koa = require('koa')
const app = new Koa()
const router = require('./router')
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log(' starting at port 3000')
})
router.js
const Koa = require('koa')
const Router = require('koa-router');
const app = new Koa()
const router = new Router();
const {
list,
detail
} = require('./controller/user')
router.get('/user/list', list)
router.get('/user/detail/:id', detail)
module.exports = router
cotroller/user.js
const array = [{id: 0, name: '王二'}, {id: 1, name: '赵三'}]
function list(ctx) {
ctx.body = {
data: array,
success: true
};
}
function detail(ctx) {
const id = ctx.params.id;
ctx.body = {
data: array[id],
success: true
}
}
module.exports = {
detail,
list
}
4.小结
这节我们简单的梳理了路由的概念,使用起来还是比较简单,相关代码在 https://github.com/hucheng91/koa-workshop/tree/master/koa-router 接下来我们会结合 request,response 来使用路由。