详解Koa中间件的使用和实现原理

前端 潘老师 5个月前 (11-29) 122 ℃ (0) 扫码查看

本文主要讲解关于Koa中间件的使用和实现原理相关内容,让我们来一起学习下吧!

中间件使用

了解原理之前先来看一下Koa中间件怎么使用

import Koa from 'koa'
import koaBody from 'koa-body'
import checkToken from '@middleware/checkToken'

const app = new Koa()
app.use(koaBody({
    multipart: true,
    formidable: {
        maxFileSize: 50 * 1024 * 1024
    }
))
app.use(checkToken())
app.listen(port, () => {
    console.log('server running')
})

通过Koa实例的use方法,把中间件函数的调用作为参数传递进去(调用的时候可以传递一些参数)。所以中间件函数的返回值是一个函数。Koa会根据use调用中间件的先后顺序执行中间件函数。接下来看一下中间件的实现。

中间件实现

export const checkToken = () => {
  return async (ctx, next) => {
    const { authorization } = ctx.request.headers // 在此中间件之前使用了koa-body中间件
    try {
      const data = await check(authorization)
      ctx.userInfo = data.user_info
    } catch (e) {
      ctx.body = {
        code: -1,
        err: 'token校验失败'
      }
      return
    }
    await next()
  }
}

中间件的返回函数可以接收两个参数,第一个参数是当前请求的上下文,第二个参数可以暂时理解为下一个中间件函数,自己去控制后面中间件的调用时机。可以在最后调用,也可以在调用之后再做其它逻辑处理。类似这样:

xxx // 此中间件逻辑
await next()
xxx // 此中间件逻辑

那么为什么中间函数的返回函数可以接收到这两个参数呢?接下来就需要了解Koa的中间件实现原理了。

中间件原理

先来看一下use方法大概是做了什么(与源码的变量名可能有差异,便于理解):

class Koa{
    constructor (){
        this.middlewares = []
    }
    use(middleware){
        if (typeof middleware !== 'function') throw new TypeError('middleware must be a function!')
        this.middlewares.push(middleware)
        return this
    }
}

校验代码就先忽略掉了,use主要是把中间件函数push到一个队列面,然后返回this是为了链式调用。

另外还需要了解一下Koa的执行流程,从listen开始:

const http = require('http')

listen(){
    const server = http.createServer(this.callback())
    return server.listen(...args)
}

这里使用了node的原生模块http,把接收到的参数直接传递给原生模块的listen方法。重点是传递给createServerthis.callback是什么。查看http模块的api发现crerateServer接收的是一个requestListener请求监听函数。接下来看下callback函数的实现。

callback(){
    const fn = this.compose(this.middlewares) // 关键,中间的执行
    // ... 
    const handleRequest = (req, res) => {
        const ctx = this.createContext(req, res) // 通过createContext把原生的req和res生成ctx上下文对象
        return this.handleRequest(ctx, fn) // 注意:这个handleRequest是类的方法,不是这个局部的方法
    }
    return handleRequest // 返回监听函数
}

上面出现了三个函数,compose、createContext、类上的。先来看下compose:来自koa-compose包。以下是源码:

function compose (middleware) {
    // 一些校验可以忽略
    if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
    for (const fn of middleware) {
        if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
    }
    // 这个函数就是返回给handleRequest的主要中间件调用逻辑
    return function (context, next) {
        // ... 删掉一些边界判断,简化一下代码,更清晰
        return dispatch(0)
        function dispatch (i) {
            let fn = middleware[i] // 从队列最开始取出一个中间件函数
            try {
                // 向中间件函数fn中传递两个参数,一个context,第二个是递归下一个中间件
                // 也就是前面编写中间件时的两个参数,ctx和next
                return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
            } catch (err) {
                return Promise.reject(err)
            }
        }
    }
}

createContext这里不关心,来看handleRequest函数:

handleRequest(ctx, fnMiddleware){
    // ...
    const onerror = err => ctx.onerror(err)
    const handleResponse = () => respond(ctx)
    // ...
    return fnMiddleware(ctx).then(handleResponse).catch(onerror) // 执行上面compose中返回的函数
}

相关的代码大致就这些,通过一个流程图来让整体调用流程更加一目了然吧。

以上就是关于Koa中间件的使用和实现原理相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/11986.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】