• koa2 快速上手


    1.koa2简介:

    koa2是由express原开发人员开发的,是为了解决Express处理异步回调嵌套过深的问题的node服务端开发框架,最初是第一版被称为koa,koa存在Generator+yield不太好的地方,后来就出现第二版koa2,koa2中支持async/await,但是我express项目中也是在用async/await。

    2.koa2特点:

    支持async/await语法糖、支持洋葱模型的中间件。

    洋葱模型中间件:web服务器在处理一个又一个的请求并作出响应时,在这个过程中需要程序处理,这个处理的程序被称为中间件,多个中间件在处理的过程中是由外到内,在由内到外的过程,从请求到响应可以理解为竹签穿过洋葱,洋葱每层表示一个中间件。

    3.koa2快速上手:

    1.安装koa2:npm install koa,下载时无需写koa2,下载时会自动下koa2。

    2.创建koa对象,编写响应函数(中间件)

    3.监听端口,启动程序,具体如下代码:

    const koa = require('koa')
    // 创建koa对象
    const app = new koa()
    // 挂载中间件:(中间件实际就是一个处理并返回结果的函数,可能你不需要写return,但是ctx中也是会返回数据),实际开发中会将中间件抽离到单独的文件中,之后挂载到app上
    app.use((ctx,next) => {
      // ctx表示上下文,可以调用到请求对象和响应对象,next表示下一个中间件的执行
      console.log(ctx.request.url)
      // 设置响应体:
      ctx.response.body = 'hello word!'
      // 调用下一中间件执行: (从上到下一次执行中间件,但是是在调用next的情况下才会执行)
      console.log('第一层中间件1')
      next()
      console.log('第一层中间件2')
      return '第一层'
    })
    // 又一层中间件:
    app.use((ctx, next) => {
      console.log('第二层中间件1')
      next()
      console.log('第二层中间件2')
      return '第二层'
    })
      // 执行顺序:第一层中间件1-------第二层中间件1---------第二层中间件2--------第二层中间件1,洋葱模型,从外到里,在从内到外
    
    // 监听服务启动:
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.项目中使用koa:

    本项目也是一个demo,实际中可能有很多种处理中间件,在这里我我使用三个中间件处理数据,分别是计算耗时中间件、设置响应头中间件、处理业务中间件(登录接口实现)

    总耗时中间件:

    module.exports = async (ctx, next) => {
      // 每次请求先记录一次时间:
      const start = Date.now()
      // 让内层中间件执行,此过程会耗时,内层中间件执行完后,下面的代码才会执行,所以在next后面再次获取当前时间,之后做减法就是总耗时
      await next()//通常情况下中间件中有异步,所以这里使用async/await
      const end = Date.now()
      const totalTime = end - start
      // 将总耗时设置给响应头:
      ctx.set('X-Response-Time', totalTime + 'ms')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    设置响应头中间件:

    module.exports = async (ctx, next) => {
      // 统一设置响应数据类型为:json格式
      const contentType = 'application/json; charset=utf-8'
      ctx.set('Content-Type', contentType)
      // 解决跨域:
      ctx.set('Access-Control-Allow-Origin','*')
      ctx.set('Access-Control-Allow-Methods','OPTIONS, GET, PUT, POST, DELETE')
      await next()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    app.js入口文件:

    // 引入计算总耗时的中间件:
    const duiringTime = require('./middleware/duringtime')
    // 引入设置响应头的中间件:
    const setResponseHeader = require('./middleware/setresponseheader')
    // 引入解析请求体的中间件:koa-bodyparser
    const bodyParser = require('koa-bodyparser')
    // 导入登录路由
    const loginserve = require('./serves/loginserve')
    
    const Koa = require('koa')
    // 创建Koa实例对象:
    const app = new Koa()
    
    // 绑定计算耗时的中间件:
    app.use(duiringTime)
    // 挂载设置响应头的中间件:
    app.use(setResponseHeader)
    // 挂载bkoa-bodyparser中间件:如果不配置的话在路由页就不能通过ctx.request.body拿到请求体
    app.use(bodyParser())
    
    // 挂载登录接口路由:
    app.use(loginserve.routes())
    
    // 绑定端口号:
    app.listen(3000,() => {
      console.log('serve is running at 3000!')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    loginserve登录接口文件:

    // 引入路由模块:
    const Router = require('koa-router')
    // 引入数据库配置
    const connection = require('../config/mysqldbconfig')
    //引入token工具
    const {createToken} = require('../commethods/creattoken')
    // 创建路由实例:
    const router = new Router({prefix:'/api'})//里面可接收一个对象,可以设置些默认配置,如{prefix:'/api'}设置路由匹配前缀
    
    // 实现一个登录接口:
    // 在创建路由时默认加了前缀 /api ,下面router.post('/login', ()=>{})可直接匹配:'/api/login'路由
    router.post('/login',async (ctx, next) => {
      let {userName, userPassword} = ctx.request.body
      let sql = 'SELECT `user_password`,`user_id`,`user_name` FROM theuser WHERE user_name = "'+userName+'"'
      // 这里不能像express中直接对数据库进行操作,否则响应拿不到查询结果,这里必须使用异步,将查询封装到一个异步函数中,之后在这里使用await调用封装的函数,如:
      // 封装查询函数(可以抽离到单独的文件中):
      function isLogin (sql){
        return new Promise((resolve,reject) => {
          connection.query(sql,(error, result) => {
            if (error) {
              reject()
            } else {
              resolve(result)
            }
          })
        })
      }
      // 调用查询函数
      let results = await isLogin(sql)
      // 响应:用户名存在时:
      if (results.length > 0) {
        // 且密码正确时:
        if (results[0].user_password === userPassword) {
          // 生成token规则
          let rule = {id:results[0].user_id,naems:results[0].user_name}
          // 生成token并响应:
          let token = createToken(rule)
          ctx.body = {cod: 200, msg: '登录成功!', userId: results[0].user_id, token: token}
        } else {
          ctx.body = {cod: 201, msg: '密码错误!'}
        }
      }  else {
        // 用户名不存在时:
        ctx.body ={cod: 202, msg: '用户不存在!'}
      }
    
      // 经测试,直接return一个Promise也可以在响应时拿到结果,查阅资料都是以上方式实现的
      return new Promise((resolve,reject) => {
        connection.query(sql, (error, results) => {
          try {
            if (error) {
              throw error
            } else {
              // 用户名存在时:
              if (results.length > 0) {
                // 且密码正确时:
                if (results[0].user_password === userPassword) {
                  // 生成token规则
                  let rule = {id:results[0].user_id,naems:results[0].user_name}
                  // 生成token并响应:
                  let token = createToken(rule)
                  ctx.body = {cod: 200, msg: '登录成功!', userId: results[0].user_id, token: token}
                } else {
                  ctx.body = {cod: 201, msg: '密码错误!'}
                }
              }  else {
                // 用户名不存在时:
                ctx.body ={cod: 202, msg: '用户不存在!'}
              }
              resolve()
            }
          } catch (err){
            reject()
            console.log('loginserve文件login登录接口错误:' + err)
          }
        })
      })
    })
    
    // 导出路由
    module.exports = router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    提示:数据库配置文件,生成token的文件我这里没有粘出

    提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者 删除。

    笔者:苦海

  • 相关阅读:
    Sentinel注解@SentinelResource详解
    Linux基础篇之文件系统
    nginx常用命令
    【RuoYi-Vue-Plus】学习笔记 41 - Easy Excel(一)Excel 2003(*.xls)导入流程分析(源码)
    程序员基础能力系列
    山西毕业主题研学夏令营
    AcWing 803. 区间合并——算法基础课题解
    Archery- SQL审核查询平台告警通知设置
    SpringBoot 项目,三种方式实现打印 sql 日志
    @Async注解的坑
  • 原文地址:https://blog.csdn.net/weixin_46758988/article/details/127586806