實現express 中間件原理

完整代碼: github 倉庫地址

1. 中間件原理: 核心handle方法

express_test.js 文件:

// express 中間件原理
const http = require('http')
const slice = Array.prototype.slice

class LikeExpress {
    constructor() {
        // 存放中間件的列表
        this.routes = {
            all: [],
            get: [],
            post: []
        }
    }

    register(path) {
        // 註冊中間件的方法: 判斷第一個參數是不是路由(字符串),
        const info = {}
        if (typeof path === 'string') {
            info.path = path
            // 從第二個參數開始轉化成數組
            info.stack = slice.call(arguments, 1)
            //另一種寫法 :  
            // info.stack = [...arguments].slice(1) 
        } else {
            info.path = '/'
            info.stack = [...arguments]
        }
        return info
    }

    use() {
        //  將所有參數 都傳入register
        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)
    }

    // 獲取匹配的url
    match(method, url) {
        let stack = []
        if (url === '/favicon.ico') {
            return stack
        }
        let curResult = []
        curResult = curResult.concat(this.routes.all)
        curResult = curResult.concat(this.routes[method])
        curResult.forEach(item => {
            if (url.indexOf(item.path) === 0) {
                // url = '/api/get-cookie 且 item.path === '/'
                //                        且 item.path === '/api'
                //                        且 item.path === '/api/get-cookie'
                stack = stack.concat(item.stack)
            }
        })
        return stack
    }

    //   核心 next 的機制
    handle(req, res, stack) {
        const next = () => {
            //  獲取第一個匹配的中間件
            const middleware = stack.shift()
            if (middleware) {
                // 執行中間件函數
                middleware(req, res, next)
            }
        }
        next()
    }

    callback () {
        return (req, res) => {
            res.json = (data) => {
                res.setHeader('Content-type', 'application/json')
                res.end(
                    JSON.stringify(data)
                )
            }
            const url = req.url
            const method = req.method.toLowerCase()
            const resultList = this.match(method, url)
            this.handle(req, res, resultList)
        }
    }

    listen(...args) {
        // 創建http服務 並監聽
        const server = http.createServer(this.callback())
        server.listen(...args)
    }
}

// 工廠函數
module.exports = () => {
    return new LikeExpress()
}

2. 使用自行封裝express

test.js文件:

//  測試自定義的express 
const express = require('./express_test')

// http請求實例
const app = express()

app.use((req, res, next) => {
    console.log("請求開始...",req.url, req.method)
    next()
})

app.use((req, res, next) => {
    // 假設在處理cookie
    console.log("處理 cookie")
    req.cookie = {
        userId: 'user233'
    }
    next()
})


app.use('/api',(req, res, next) => {
    console.log("處理/api路由")
    next()
})

function loginCheck (req, res, next) {
    setTimeout(() => {
        console.log("模擬登陸成功")
        next()
    })
}

app.get('/api/get-cookie',loginCheck, (req, res , next) => {
    console.log('get /api/get-cookie')
    res.json({
        error: 0,
        data:req.cookie
    })
    next()
})

app.listen(8233, () => {
    console.log("監聽 8233")
})

3. 驗證

運行: node test.js
打開瀏覽器輸入: localhost:8233/api
localhost:8233/api/get-cookie
查看控制檯,感受這個流程。

發佈了67 篇原創文章 · 獲贊 29 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章