通过代码来理解express中间件原理

likeExpress.js 如下所示:

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

class LikeExpress {

  constructor() {
    this.routes = {
      all: [],  // 存放通用的中间件
      get: [],  // 存放get请求的中间件
      post: []  // 存放Post请求的中间件
    }
  }

   // 注册中间件函数
  register(path) {
    let info = {}
    // 如果传进来的路径是一个字符串类型的 则将第2个参数开始的参数都作为中间件
    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() {
    // 如果实例调用了use方法,则注册通用的中间件 放到all数组中
    const info = this.register.apply(this, arguments);
    this.routes.all.push( info );
  }

  get() {
    // 如果实例调用了get方法 则将中间件放进get数组中
    const info = this.register.apply(this, arguments);
    this.routes.get.push(info)
  }

  post() {
    // 如果实例调用了post方法 则将中间件放进post数组中
    const info = this.register.apply(this, arguments);
    this.routes.post.push( info );
  }


  // 匹配函数
  match( method, url ) {
    let stack = []  // 用来存放中间件的数组
    // 当我们访问网站的时候会发一个/favicon.ico请求 所以需要做一下处理
    if (url === '/favicon.ico') {
      return stack
    }
    let curRoutes = []  // 声明一个存放当前路由的数组
    curRoutes = curRoutes.concat(this.routes.all)  // 先拿到公共的路由 也就是app.use()
    curRoutes = curRoutes.concat(this.routes[method])  // 再通过传进来的method拿到对应请求的路由

    // 循环遍历当前拿到的路由数组
    curRoutes.forEach((routeInfo) => {
      // 判断客户端的url地址 和 当前路由的path能不能匹配上 能匹配上说明注册了这个中间件
      if (url.indexOf( routeInfo.path ) === 0) {
        // 把匹配上的中间件都给stack数组
        stack = stack.concat(routeInfo.stack)
      }
    })
    return stack
  }

// 核心代码
  handle(req, res, stack) {
    // 我们创建一个next函数
    const next = () => {
      // 首先从中间件列表中拿到第一个中间件
      const middleware = stack.shift()
      // 如果有中间件,则执行中间件,并且把req,res,next传递给它做参数。
      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)
    }
  }

   // 利用node.js提供的http模块启一个http服务。
  listen(...args) {
    const server = http.createServer(this.callback())
    server.listen(...args)
  }
}

module.exports = () => {
  return new LikeExpress()
}

让我们调用一下这个袖珍版的express试试效果

test.js

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

const app = express();

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

app.use((req, res, next) => {
  console.log('处理cookie...')
  req.cookie = {
    userId: 'abcd123'
  }
  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(8900, () => {
  console.log('server is runing at port 8900.')
})

最后,我们看一下效果 使用node启动服务:

通过代码来理解express中间件原理_第1张图片

通过代码来理解express中间件原理_第2张图片

事实证明,我们写的袖珍版express还是通过了考验的。

你可能感兴趣的:(koa2)