实现简易express

73 阅读1分钟

使用http模块

实现步骤

  1. 创建 SimpleExpress 构造函数,实现usegetpostlisten等方法,存储传入的路由中间件
class SimpleExpress {
  constructor(){
    this.routes = {
      all: [],
      get: [],
      post: []
    }
  }
  use(){}
  get(){}
  post(){}
  listen(){}
}
  1. 收集并注册中间件register
class SimpleExpress {
	/* ... */
  use(...args){
    this.routes.all.push(this.register(...args))
  }
  get(...args){
    this.routes.get.push(this.register(...args))
  }
  post(...args){
    this.routes.post.push(this.register(...args))
  }

  // 返回收集的路由和中间件
  register(...args){
    const path = args[0] // 第一个参数判断
    const info = {}
    if(typeof path === 'string') {
      info.path = path
      info.stack = Array.prototype.slice.call(args, 1) // 截取第二位及以后
    }else {
      info.path = '/'
      info.stack = Array.prototype.slice.call(args, 0)
    }
    return info
  }
}
  1. 通过 path 匹配所需中间件match
class SimpleExpress {
	/* ... */
  match(method, url){
    if(url !== '/favicon.ico') { // 过滤ico图标
      return []
    }
    let curRoutes = []
    curRoutes = curRoutes.concat(this.routes.all) // 公共中间件
    curRoutes = curRoutes.concat(this.routes[method]) // 通用方法中间件
    let stack = []
  	curRoutes.forEach( (routeInfo) => { 
      if(url.indexOf(routeInfo.path)  === 0 ){ 
        // 路径匹配
        stack = stack.concat(routeInfo.stack)
      }
    })
    return stack
  }
}
  1. 执行匹配的中间件,实现handle即核心next()函数
class SimpleExpress {
	/* ... */
  callback(){
    return (req, res, next) => {
      res.json = (data) => {
        res.setHeader('Content-type', 'application/json')
        res.end(JSON.stringify(data))
      }

      const url = req.url
      const method = req.method.toLowerCase()
      const resultRoutes = this.match(method, url)

      this.handle(req, res, resultRoutes)
    }
  }
	// 实现 next() 核心
  handle(req, res, stack){
    const next = () => {
      const firstRoute = resultRoutes.shift()
      if(firstRoute){
        handle(req, res, next)
      }
    }
    next()
  }
}
  1. 实现listen创建服务 监听端口
const http = require("http")
class SimpleExpress {
	/* ... */
  listen(...args){
    const app = http.createServer(this.callback())
    server.listen(...args)
  }
}

全部代码

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

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

  register(path){
    const 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(){
    this.routes.push(this.register.apply(this.atguments))
  }
  get(){
    this.routes.push(this.register.apply(this.atguments))
  }
  post(){
    this.routes.push(this.register.apply(this.atguments))
  }

  listen(...args){
    const server = http.createServer(this.callback())
    server.listen(...args)
  }
  // 传入
  callback(){
    return (req, res) => {
    	res.json(
        res.setHeader('Content-type', 'application/json')
        res.end(JSON.stringify(data))
      )
      const url = req.url
      const method = req.method.toLowerCase()
      // 根据 url、method获取匹配的中间件
      const routeList = this.match(method, url)
      // 执行中间件
      this.handler(req, res, routeList)
    }
  }
  // 匹配中间件
  match(method, url){
    if(url.indexOf('/favicon.ico') === 0){ return [] }
    const currentRoutes = [].concat(this.routes.all, this.routes[method])
    let stack = []
    currentRoutes.forEach( (route) => {
      if(url.indexOf(route.path) === 0){ 
        stack = stack.concat(route.stack) 
      } 
    })
    return stack
  }

  // 执行中间件 实现 next()
  handler(req, res, routes){
    const next = (req, res) => {
      const middleware = routes.shift()
      if(middleware) { middleware(req, res, next) }
    }
    next()
  }
}

总结

  • 收集所有使用的中间件

  • 通过methodurl匹配 path 路径

  • 核心: 实现next()执行所有匹配的中间件