express中间件原理及实现

2020-03-08  本文已影响0人  jackie季

中间件是一种拦截器的思想,用于在某个特定的输入输出之间添加一些额外的处理,它最大的特点就是,一个中间件处理完,再传递给下一个中间件,App实例在运行过程中,会调用一系列的中间件。
每个中间件可以从App实例中接收三个参数,依次是request对象(请求)、response对象(响应)、next函数(执行下一个中间件),每一个匹配的中间件都可以对request对象进行加工,并且决定是否调用next方法来执行下一个中间件。
中间件是在管道中执行的,你可以想象一个真实的水管道,水从一端注入,到达另一端之前会经过各种仪器、阀门,这里的顺序也是很重要的一点。在express中,调用app.use可以插入一个新的管道。

分析一下中间件的原理:
1.app.use用来注册中间件,先收集起来;
2.遇到HTTP请求,根据path和method进行匹配,判断触发哪些中间件;
3.next机制,即上一个通过next触发下一个;

那么,我们就来简单用代码实现一下:

const http = require('http')
const slice = Array.prototype.slice

class EasyExpress {
    constructor() {
        this.routes = {
            all: [],
            get: [],
            post: []
        }
    }

    // 注册中间件
    register(path) {
        let info = {}
        // 判断第一个参数是否为路由
        if (typeof path === 'string') {
            info.path = path
            info.stack = slice.call(arguments, 1)
        } else {
            info.path = '/'
            info.stack = slice.call(arguments, 0)
        }
        return info
    }

    use() {
        const info = this.register.apply(this, arguments)
        this.routes.all.push(info)
    }

    get() {
        const info = this.register.apply(this, arguments)
        this.routes.get.push(info)
    }

    post() {
        const info = this.register.apply(this, arguments)
        this.routes.post.push(info)
    }

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

    callback() {
        return (req, res) => {
            let { url, method } = req
            method = method.toLowerCase()

            let stack = []
            const routeList = []

            routeList = routeList
                .concat(this.routes.all)
                .concat(this.routes[method])

            // 寻找匹配的中间件
            routeList.forEach(routeInfo => {
                if (url.indexOf(routeInfo.path) === 0) {
                    stack = stack.concat(routeInfo.stack)
                }
            })

            this.handler(req, res, stack)
        }
    }

    // 处理stack
    handler(req, res, stack) {
        const next = () => {
            const fn = stack.shift()
            if (fn) {
                fn(req, res, next)
            }
        }
        next()
    }
}

next 方法不断的取出stack中的“中间件”函数进行调用,同时把next 本身传递给“中间件”作为第三个参数,每个中间件约定的固定形式为 (req, res, next) => {}, 这样每个“中间件“函数中只要调用 next 方法即可传递调用下一个中间件。

上一篇 下一篇

猜你喜欢

热点阅读