百来行代码简洁解析express中间件原理(并测试)

下面代码有详细注释

文件like-express.js

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

class LikeExpress{
    constructor(){
        // 存放中间件的列表
        this.routes ={  
            all:[], // app.use(...)
            get:[], // app.get()
            post:[] // app.post
            // 还有其他方法比如put,patch等
            
        }
    }
    
	//用register存储获得info存储中间件,下面的use(),get(),post()会分别继承方法将不同的中间件存入
	//上面的routes的不同数组中
    register(path){
        const info = {} //当前的注册信息
        if(typeof path === 'string'){//qpp.use第一个参数没有的话就是命中了根路由
            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) //把当前函数所有的参数都放在register中并返回info
        // 推入routes.all中
        this.routes.all.push(info)
    }

    get(){
        const info =this.register.apply(this,arguments) //把当前函数所有的参数都放在register中并返回info
        // 推入route. get中
        this.routes.get.push(info)
    }

    post(){
        const info =this.register.apply(this,arguments) //把当前函数所有的参数都放在register中并返回info
        
        this.routes.post.push(info)
    }
//----------------------------------------------------------------

//macth主要用于匹配get,post等方法响应的中间件
    match(method,url){
        let stack = []
        if(url==='/favicon.icon'){
            return stack  //返回空
        }

        // 获取routes
        let curRoutes = []
        curRoutes = curRoutes.concat(this.routes.all)
        // 获取post和get方法 并将两个数组拼接
        curRoutes = curRoutes.concat(this.routes[method])


        curRoutes.forEach(routeInfo => {
            // 判断两路径是否符合 
            if(url.indexOf(routeInfo.path) === 0){
                stack =stack.concat(routeInfo.stack)
            }
        })
        return stack
    }
    
    // 核心的next的机制实现 
    handle(req,res,stack){
        const next = ()=>{
            // 拿到第一个匹配的中间件
            // 注意shift()方法是拿到值 并删除 所以只要next()方法就会一直执行下去
            const middleware = stack.shift()
            if(middleware){
            // 执行中间件的函数
                middleware(req,res,next)
            }
        }
        next()
    }

    callback(){
        return (req,res)=>{
            // res.json函数就是下面代码组成的
            res.json = (data)=>{
                // 设置json格式
                res.setHeader('Content-type','application/json')
                res.end(
                    JSON.stringify(data)
                )
            }
            const url = req.url
            const method = req.method.toLowerCase()
            // 用match函数获得中间件的列表
            const resultList =this.match(method,url)
            
            this.handle(req,res,resultList)

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

    }

}


// 工厂函数
module.exports = () =>{
    return new LikeExpress()
}

接下来有一段测试代码,测试上述功能是否可以跑通
文件test.js

const express = require('./like-express')



// 本次 http 请求的实例
const app = express()

app.use((req, res, next) => {
    console.log('请求开始...', req.method, req.url)
    next()
})

app.use((req, res, next) => {
    // 假设在处理 cookie
    console.log('处理 cookie ...')
    req.cookie = {
        userId: 'abc123'
    }
    next()
})

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

app.get('/api', (req, res, next) => {
    console.log('get /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({
        errno: 0,
        data: req.cookie
    })
})

app.listen(3000, () => {
    console.log('server is running on port 8000')
})

用node test 运行之后
打开本地的 http://127.0.0.1:3000/api/get-cookie 情况如下

在这里插入图片描述

在这里插入图片描述
说明基本完成了功能

注:菜鸟学习笔记,如有错误,感谢评论指正,如有侵权,商讨删除

发布了158 篇原创文章 · 获赞 33 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览