Koa入门(二)搭建 Koa 程序

648 阅读5分钟

1 项目搭建

1.1 初始化目录

安装 mkdir koa-demo && cd koa-demo && npm init -y && npm i koa --save && code .

package.json 文件中配置:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "start": "node index.js"
}

1.2 根目录新建 index.js 文件

const Koa = require('koa')
const app = new Koa()

// 中间件
app.use((ctx) => {
  // ctx.body 相当于 http.createServer((req, res) => { res.end('你好,测不准') })
  ctx.body = '你好,测不准'
})

app.listen(3000, () => {
  console.log('监听3000端口')
})

ctx.body 就是返回给前端的数据,之前的开发是 MVC 模式,后端直接渲染完,返回给前端,你可以这么写 ctx.body = '<h1>你好,测不准</h1>',这样页面就可以直接渲染标签;但是现在的项目都是前后端分离的,后端直接返回 JSON 数据,前端拿到之后渲染,所以 Koa 返回的数据是这样的 ctx.body = [{name: 'uncertainty'}, {name: '测不准'}],希望我的表达让您理解。

执行 npm start

打开浏览器,输入 http://localhost:3000/

当然大家现在可以使用浏览器查看,因为是 get 请求,但是推荐大家安装 postman,后期可以方便测试 post 请求或者上传文件功能。postman下载地址

1.3 使用 postman 请求 http://localhost:3000/

但是这里每次改动代码都需要重新启动服务,不是很方便,这里我们安装 nodemon 辅助工具,更改之后会自动刷新,全局或者当前项目下安装都可以,我这里全局安装下 npm i nodemon -g

修改启动命令为 "start": "nodemon index.js"nodemon 会帮我们监听 js, mjs, json 文件的变化,自动启动程序)

2 实现简单 Koa

大家都知道,Koa 是对 node 的封装,先来个简单的服务实现:

  • 新建文件 application.js 使用 Koa 时是 new 的实例,所以需要实现个类,listen 方法监听端口,use 方法实现挂载中间件,如下:
let http = require('http')

class Application{
  constructor() {
    this.callbackFunc
  }

  // 开启http srever 传入callback
  listen(...args) {
    let server = http.createServer(this.callback())
    server.listen(...args)
  }

  /**
   * 挂载回调函数
   * @param {*} fn 回调处理函数
   */
  use(fn) {
    this.callbackFunc = fn
  }

  /**
   * 获取http server所需的callback函数
   */
  callback() {
    return (req, res) => {
      this.callbackFunc(req, res)
    }
  }
}
module.exports = Application
  • 新建 example.js 文件 把刚创建的 application.js 文件引入
let simpleKoa = require('./application')
let app = new simpleKoa()

app.use((req, res) => {
  res.writeHead(200)
  res.end('hello, uncertainty')
})

// 这次监听 8000
app.listen(8000, () => console.log('监听8000端口'))
  • 执行命令 nodemon example.js

3 中间件

Koa 是一个中间件框架,本身没有捆绑任何中间件(核心代码简洁)。本身支持的功能并不多,功能都可以通过中间件拓展实现。通过添加不同的中间件,实现不同的需求,从而构建一个 Koa 应用。Koa 的中间件就是函数,现在基本都是 async 函数。

  • app.use() 是用于注册中间件并且必须是生成器函数(源码中有判断,后面大版本会移除,2.0 为了向下兼容)
use(fn) {
  if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
  if (isGeneratorFunction(fn)) {
    deprecate('Support for generators will be removed in v3. ' +
              'See the documentation for examples of how to convert old middleware ' +
              'https://github.com/koajs/koa/blob/master/docs/migration.md');
    fn = convert(fn);
  }
  debug('use %s', fn._name || fn.name || '-');
  this.middleware.push(fn);
  return this;
}

生成器函数:generatorES6 新增的一个特殊函数,通过 function* 声明,函数体内通过 yield 来指明函数的暂停点,该函数返回一个迭代器,并且函数执行到 yield 语句前面暂停,之后通过调用返回的迭代器 next() 方法来执行 yield 语句

我们也可以使用生成器函数做中间件(不推荐):

const Koa = require('koa')
const app = new Koa()

app.use(function *(next){
  console.log(1)
  yield next;
  console.log(3)
  this.body = '你好,测不准啊'
})
app.use(function *(next){
  console.log(2)
  yield next
})

app.listen(3000, () => {
  console.log('监听3000端口')
})

  • Koa 的中间件通过一种更加传统的方式进行级联,摒弃了以往 node 频繁的回调函数造成的复杂代码逻辑

  • Koa 会把很多中间键函数组成一个处理链,每个中间键函数都可以做一些自己的事情,然后用 next() 来调用下一个中间键函数

  • 中间键必须是一个函数,可为异步函数:通过es7中的async和await来处理

  • use 内部封装了两个对象:ctx, next

  1. ctxcontext 的一般叫成上下文,主要包括 requestresponsebodyhttp 协议中的响应体,header 是指响应头,如果要抛异常可以直接使用 ctx.throw(500, '接口异常')ctx.status 设置状态码,ctx.url 获取请求 URL 等。
  2. next 起到串联中间件的作用,通过调用 next 函数,把执行权交给下一个中间件。最后一个中间件不使用该函数。

4 编写自己的中间件

4.1 log 中间件

日志模块也是线上不可缺少的一部分,完善的日志系统可以帮助我们迅速地排查出线上的问题。通过 Koa 中间件,我们可以实现属于自己的日志模块(当然可以直接用社区现成的库):

  • 新建 logger.js
const fs = require('fs')
module.exports = (options) => async (ctx, next) => {
  const startTime = Date.now()
  const requestTime = new Date()
  await next()
  const ms = Date.now() - startTime
  let logout = `${ctx.request.ip} -- ${requestTime} -- ${ctx.method} -- ${ctx.url} -- ${ms}ms`
  // 输出日志文件
  fs.appendFileSync('./log.txt', logout + '\n')
}
  • 入口文件引入 logger.js 文件
const Koa = require('Koa')
const app = new Koa()
const logger = require('./logger')

app.use(logger())

app.listen(3000, () => {
    console.log(`Server port is 3000.`)
})

4.2 token 验证

前后端分离开发,我们常采用 JWT 来进行身份验证,其中 token 一般放在 HTTP 请求中的 Header Authorization 字段中(后面会介绍),每次请求后端都要进行校验,不可能每个接口都写判断,Koa 通过编写中间件来实现 token 验证。

  • 创建 token.js
module.exports = (options) => async (ctx, next) {
  try {
    // 获取 token
    const token = ctx.header.authorization
    if (token) {
      try {
          // verify 函数验证 token,并获取用户相关信息
          await verify(token)
      } catch (err) {
        console.log(err)
      }
    }
    // 进入下一个中间件
    await next()
  } catch (err) {
    console.log(err)
  }
}
  • 入口文件引入 token.js 文件
const Koa = require('Koa')
const app = new Koa()
const token = require('./token')

app.use(token())

app.listen(3000, () => {
    console.log(`Server port is 3000.`)
})

喜欢的朋友可以关注下公众号:与前端沾边,2021 年一起学习进步。