Simple implementation principle of Express Middleware

Posted by j4IzbInao on Fri, 08 Nov 2019 16:15:58 +0100

After understanding the use of Express in the previous article, I will summarize the simple implementation principle of Express middleware.

We know that Express middleware is a function by function, so how to make these functions execute orderly? We need to call the next function. In fact, the next function calls the next middleware function.

The following code implements the simple app.use registration middleware, as well as the get and post middleware. The same is true for middleware implementation of other request modes

Core code:

const next = () => {
    const stack = stacks.shift()
    if(stack) {
        stack(req, res, next)
    }
}
next()

stacks is an array queue that holds all the middleware functions that meet the rules. Follow the first in, first out principle. That is, the first registered middleware function is executed first.

Implementation code

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

 class Express {
     constructor() {
        this.router = {
            all: [], // Match all middleware functions
            get: [],
            post: []
        }
     }

     /**
      * Integrate middleware here
      * @param {string} path 
      * @returns {object}
      */
     middlewareHandler(path) {
        const info = {}
        if (typeof path === 'string') {
            info.path = path
            info.stack = slice.call(arguments, 1)  // Middleware array
        } else {
            info.path = '/'
            info.stack = slice.call(arguments, 0)
        }

        return info
     }

     use() {
        const allStack = this.middlewareHandler(...arguments)
        this.router.all.push(allStack)
     }

     get() {
        const getStack = this.middlewareHandler(...arguments)
        this.router.get.push(getStack)
     }

     post() {
        const postStack = this.middlewareHandler(...arguments)
        this.router.post.push(postStack)
     }

      /**
       * 
       * @param {string} method 
       * @param {string} url
       * @returns {Array} 
       */
     accordStack(method, url) {
        let stacks = []
        stacks = stacks.concat(this.router.all)
        stacks = stacks.concat(this.router[method])
        return stacks
        .filter(stack => {
            return url.indexOf(stack.path) !== -1
        }).map(item => item.stack[0])
     }

     handler(req, res, stacks) {
         // Function expression
        const next = () => {
            const stack = stacks.shift()
            if(stack) {
                stack(req, res, next)
            }
        }
        next()
     }

     callback() {
        return (req, res) => {
            res.json = data => {
                res.setHeader('Content-Type', 'application/json')
                res.end(JSON.stringify(data))
            }
            
            // Get the request method and url, and filter the middleware functions
            const {method, url} = req
            const stacks = this.accordStack(method.toLowerCase(), url)
            this.handler(req, res, stacks)
        } 
     }

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

 // Factory mode, export an instance object
module.exports = () => {
    return new Express()
}

Topics: Javascript JSON