前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Koa入门(三)Koa 路由

Koa入门(三)Koa 路由

原创
作者头像
测不准
发布2021-04-14 16:00:10
1.2K0
发布2021-04-14 16:00:10
举报

Koa 应用中,通常会使用 koa-router 模块,提供对路由的支持。那为什么需要路由呢?做前后端分离开发的朋友都遇到过,对接接口的时候后台都会提供一个地址,请求这个地址,传相应参数就能实现相应地数据处理。你可以把这个接口理解为路由的地址

1 koa-router 使用

1.1 安装引入

  • npm i koa-router --save 新建 router.js 文件const Koa = require('koa') const Router = require('koa-router') const app = new Koa() const router = new Router() router.get('/', ctx => { ctx.body = '你好,测不准' }) // 注册路由 app.use(router.routes()) // 自动丰富 response 相应头,当未设置响应状态(status)的时候自动设置,在所有路由中间件最后设置(全局,推荐),也可以设置具体某一个路由(局部),例如:router.get('/index', router.allowedMethods()); 这相当于当访问 /index 时才设置 app.use(router.allowedMethods()) app.listen(3000, () => { console.log('监听3000端口') })增删改查的路由// 获取列表,返回数组 router.get('/', ctx => { ctx.body = [] }) // 根据id获取某一项,返回对象 router.get('/:id', ctx => { ctx.body = {} }) // 新建一项 router.post('/', ctx => { ctx.body = {} }) // 根据id更新某项 router.put('/:id', ctx => { ctx.body = {} }) // 根据id删除某一项 router.delete('/:id', ctx => { ctx.body = {} })koa-router(支持基础的常用的 http 方法('HEAD','OPTIONS','GET','PUT','PATCH','POST','DELETE'),生僻的请求方法会返回 501 状态码)
  • 如果如要在路由前增加逻辑判断,例如是否有权限访问,可以在路由中插入中间件// 定义中间件 const auth = async (ctx, next) => { if (ctx.url !== '/users') { ctx.throw(401) } await next() } // 注入 router.post('/users', auth, ctx => { ctx.body = ctx.request.body })

1.2 参数获取

  • get 获取参数 (ctx.params 或者 ctx.request.params)// 获取单个参数 router.get('/:id', ctx => { ctx.body = ctx.params }) // 获取多个参数 router.get('/:id/:age', ctx => { ctx.body = ctx.request.params })
  • post 获取参数(ctx.request.body) 获取参数需要用到第三方中间件,koa-bodyparsernpm i koa-bodyparser --saveconst Koa = require('koa') const Router = require('koa-router') const bodyParser = require('koa-bodyparser') const app = new Koa() const router = new Router() router.post('/', ctx => { ctx.body = ctx.request.body }) // 放到路由前面 app.use(bodyParser()) app.use(router.routes()) app.use(router.allowedMethods()) app.listen(3000, () => { console.log('监听3000端口') })

2 路由封装

我们开发项目不可能都把路由写在 index.js 入口文件中,需要使用单独的路由文件夹管理,只在入口文件引入一个即可。

  • 根目录创建 app/routes/home.jsconst Router = require('koa-router') const router = new Router() router.get('/', (ctx) => { ctx.body = '首页' }) module.exports = routerconst Router = require('koa-router') // **配置路由前缀** const router = new Router({ prefix: '/users' }) router.get('/', (ctx) => { ctx.body = '用户列表' }) router.get('/:id', ctx => { ctx.body = `用户id:${ctx.params.id}` }) module.exports = router因为一个项目中的路由的接口会有很多,不可能在入口 index.js 中每个都要引入,所以需要对所有路由做个处理,这样在入口文件中只要导入一个即可
  • 创建 app/routes/users.js
  • 创建 app/routers/index.jsconst fs = require('fs') module.exports = app => { // 读取当前目录下所有文件 fs.readdirSync(__dirname).forEach(file => { // 除去归纳的 `index.js` 文件,其他的都要注册到 `app` 中 if (file === 'index.js') return const router = require(`./${file}`) app.use(router.routes()).use(router.allowedMethods()) }) }... // 引入 const routing = require('./routes') ... // 使用 routing(app) ...
  • app/index.js 中引入

3 使用控制器

我们现在是把数据处理(虽然还没写数据,写死的哈)和路由放在一起,这样不便于维护处理,而且后面数据处理复杂了,会显得路由文件十分臃肿,我们应该把路由文件和数据处理分开来看。

  • 创建 app/controllers/home.jsclass HomeCtl { index(ctx) { ctx.body = '这是主页' } } module.exports = new HomeCtl()
  • 修改 app/routes/home.jsconst Router = require('koa-router') const router = new Router() // const { index } = require('../controllers/home') router.get('/', index) module.exports = router
  • 创建 app/controllers/users.jsclass UsersCtl { async find(ctx) { // 操作数据库一定要 await ctx.body = '用户列表' } async findById(ctx) { ctx.body = `用户id:${ctx.params.id}` } } module.exports = new UsersCtl

附:洋葱模型

通过前两节的介绍相信大家已经对 Koa 的执行机制有所了解,尤其是 await next() 执行完之后的中间件又会重新回来继续执行未执行的逻辑,不经感慨作者设计思路的巧妙。我们来简单了解下实现思路,核心代码在 koa-componse 中,代码也不多,我们简单学习下:

function compose(middleware) {
  return function(context) {
    // 我们其实只是返回了中间件的第一项,但是第一项执行过程中会递归执行后面的
    return dispatch(0);

    function dispatch(i) {
      let fn = middleware[i];
      try {
        // 每次执行 fn 函数时候,fn 中包含 next 函数(dispatch(i + 1)),所以有了先执行 next 代码,再回过头来执行该中间件下面的代码
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1));
      } catch (err) {
        return Promise.reject(err);
      }
    }
  };
}
  • 定义测试的中间件数组const middleware = [ async (ctx, next) => { console.log("1"); await next(); console.log("3"); }, async (ctx, next) => { console.log("2"); } ];我们可以看到,在中间件中执行 await next(),其实就是执行:await dispatch.bind(null, i + 1)。因此看起来,当前中间件会停止自己的逻辑,先处理下一个中间件。因为每个 dispatch,都返回新的 Promise。所以 async 会等到 Promise 状态改变后再回来继续执行自己的逻辑。
  • 简单写个按洋葱模型执行的方法(不考虑上线文,只是单纯的执行)let middlewares = [ async (next) => { console.log(1) await next() console.log(3) }, async (next) => { console.log(2) await next() }, async (next) => { console.log('test') } ] function componse() { return dispatch(0) async function dispatch(i) { let fn = middlewares[i] await fn(dispatch.bind(null, i + 1)) } } componse()

下一篇我们将学习数据库的操作,如果文章对你有帮助,欢迎关注呦!谢谢阅读!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 koa-router 使用
    • 1.1 安装引入
      • 1.2 参数获取
      • 2 路由封装
      • 3 使用控制器
      • 附:洋葱模型
      相关产品与服务
      消息队列 TDMQ
      消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档