express


基本使用

import express from "express"
const app = express()
app.use(
  function (req, res, next) {
    console.log("middlware 1")
    next()
  },
  function (req, res, next) {
    console.log("middlware 2")
    next("a")
  }
)
app.use(function (req, res, next) {
  console.log("middlware 3")
  next()
})
app.get("/user", function (req, res, next) {
  // console.log(1, req.a)
  console.log("222=>", 222)
  next()
})
app.use((err, req, res, next) => {
  res.end(err)
})
app.listen(3001)

核心实现

application.js

application.js 是框架的主入口,负责启动 HTTP 服务器并处理请求。

  • 懒加载路由系统lazy_route 方法确保在需要时才加载路由系统。

  • use 方法:用于注册全局中间件。

  • listen 方法:创建 HTTP 服务器,通过调用 this.router.handle 处理每个请求。如果没有匹配的路由,则调用 done 方法结束请求,返回错误信息。

import http from "http"
import methods from "methods"
import Router from "./router/index.js"

function Application() {}

Application.prototype.lazy_route = function () {
  if (!this.router) {
    this.router = new Router() // 将路由系统进行了懒加载处理(一般都是要加载的)
  }
}
Application.prototype.use = function () {
  this.lazy_route()
  this.router.use(...arguments) //  交给路由系统来处理
}
Application.prototype.listen = function (...args) {
  const server = http.createServer((req, res) => {
    function done() {
      // 如果路由系统中的层不存在则调用此方法来结束响应
      res.end(`Cannot ${req.method} ${req.url}`)
    }
    // 交给路由系统来做匹配,如果匹配不到就调用done
    this.router.handle(req, res, done)
  })
  server.listen(...args)
}
methods.forEach((method) => {
  Application.prototype[method] = function (path, ...handlers) {
    // 让路由系统处理逻辑
    this.lazy_route()
    this.router[method](path, handlers)
  }
})

export default Application

Router.js

router.js 是路由系统的核心,管理路由栈(stack)并实现路由匹配逻辑。

  • 路由注册:为每个 HTTP 方法创建相应的路由,使用 Layer 类管理路径,并通过 dispatch 方法执行相应的路由处理函数。
  • 中间件注册use 方法用于注册全局或特定路径的中间件。
  • 请求处理handle 方法用于匹配请求路径和方法,逐层执行中间件和路由处理函数。如果存在错误,则调用错误处理中间件。
import url from "url"
import methods from "methods"
import Layer from "./Layer.js"
import Route from "./route.js"
function Router() {
  this.stack = []
}
methods.forEach((method) => {
  Router.prototype[method] = function (path, handlers) {
    // 调用类来管理路径
    let route = new Route()
    handlers.forEach((handler) => {
      route[method](handler)
    })
    let layer = new Layer(path, route.dispatch.bind(route))
    // 每个路由的层都有一个route属性,对应存放自己的真实路基的
    layer.route = route
    this.stack.push(layer)
  }
})
Router.prototype.use = function (path, ...handlers) {
  if (typeof path === "function") {
    handlers = [path, ...handlers] // path就是处理函数
    path = "/" // 如果没写路径就是 / 匹配所有的路径
  }
  handlers.forEach((handler) => {
    const layer = new Layer(path, handler)
    layer.route = undefined // 中间件没有路由这个对象
    this.stack.push(layer)
  })
}
Router.prototype.handle = function (req, res, out) {
  const { pathname, query } = url.parse(req.url, true)
  const method = req.method.toLowerCase()
  let idx = 0
  let next = (err) => {
    if (this.stack.length == idx) return out()
    let layer = this.stack[idx++] // 拿出第一个层
    if (err) {
      if (!layer.route) {
        // 有错误找中间件,而且 要找参数是4个的中间件
        if (layer.handler.length === 4) {
          layer.handler(err, req, res, next)
        } else {
          next(err) // 普通中间件继续带着错误向下走
        }
      } else {
        // 有错误但是是路由,要带着 错误继续往下走
        next(err)
      }
    } else {
      // 因为错误处理中间件定义在了 router.stack中 ,如果有err就去这个stack中查找错误处理中间件
      if (layer.match(pathname)) {
        if (layer.route) {
          // 路由
          if (layer.route.methods[req.method.toLowerCase()]) {
            // 需要匹配方法
            layer.handle_request(req, res, next) //  route.dispatch
          } else {
            next() // 方法不一致直接向下走
          }
        } else {
          // 中间件无需匹配方法, 没有错误不能执行错误处理中间件
          if (layer.handler.length !== 4) {
            layer.handle_request(req, res, next) //  route.dispatch
          } else {
            next()
          }
        }
      } else {
        next()
      }
    }
  }

  next() // 默认在路由中筛查
}

export default Router

Layer.js

layer.js 定义了路由和中间件的抽象层。每个路由或中间件对应一个 Layer 实例。

  • 匹配路径match 方法用于检查当前 Layer 是否匹配请求路径。

  • 请求处理handle_request 方法调用具体的处理函数。

// 路由中对应的层
function Layer(path, handler) {
  this.path = path
  this.handler = handler
}
Layer.prototype.match = function (path) {
  if (this.path === path) {
    return true
  }
  if (!this.route) {
    // 中间件
    if (this.path === "/") {
      // 中间件是/都能匹配
      return true
    }
    return path.startsWith(this.path + "/")
  }
  return false
}

Layer.prototype.handle_request = function (req, res, next) {
  return this.handler(req, res, next)
}
export default Layer

Route.js

route.js 负责管理每个特定路由的处理函数,并调度(dispatch)处理。

  • 方法注册:为每个路由绑定特定 HTTP 方法(如 getpost),并将处理函数存储在 stack 中。

  • 调度处理dispatch 方法根据请求方法依次执行对应的处理函数。

import methods from "methods"
import Layer from "./Layer.js"
function Route() {
  this.stack = []
  this.methods = {} // 用来描述这个route中有什么方法
}
methods.forEach((method) => {
  Route.prototype[method] = function (handler) {
    let layer = new Layer("里层的用户的逻辑不需要这个path", handler)
    layer.method = method
    this.methods[method] = true // {get:true}
    this.stack.push(layer)
  }
})

Route.prototype.dispatch = function (req, res, out) {
  let idx = 0
  const next = (err) => {
    console.log("run")
    if (err) return out(err)
    if (idx === this.stack.length) return out()
    let layer = this.stack[idx++]
    if (req.method.toLowerCase() === layer.method) {
      // 用户绑定的方法
      layer.handle_request(req, res, next)
    } else {
      next()
    }
  }
  next()
}
export default Route

文章作者: 高红翔
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 高红翔 !
  目录